diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2fb93a48c..fa9bb447a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,13 +6,14 @@ jobs: build: name: Build Smack - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: java: - - 11 + - 17 + - 21 env: - PRIMARY_JAVA_VERSION: 11 + PRIMARY_JAVA_VERSION: 21 steps: - name: Checkout @@ -52,10 +53,10 @@ jobs: - name: Install GraphViz run: sudo apt update && sudo apt install graphviz - name: Install Android SDK Manager - uses: android-actions/setup-android@v2 + uses: android-actions/setup-android@v3 - name: Install Android SDK run: | - sdkmanager "platforms;android-19" + sdkmanager "platforms;android-23" # Testing - name: Gradle Check @@ -71,15 +72,22 @@ jobs: run: ./gradlew javadocAll --stacktrace # Test Coverage Report - - name: Jacoco Test Coverage + - name: Aggregated Jacoco Test Coverage Report if: ${{ matrix.java == env.PRIMARY_JAVA_VERSION }} - run: ./gradlew jacocoRootReport coveralls - env: - COVERALLS_REPO_TOKEN: S2ecSJja2cKJa9yv45C8ZFPohXuRrTXKd + run: | + ./gradlew smack-java11-full:testCodeCoverageReport + + # Coveralls + - name: Report coverage stats to Coveralls + if: ${{ matrix.java == env.PRIMARY_JAVA_VERSION }} + uses: coverallsapp/github-action@v2 + with: + format: jacoco + file: smack-java11-full/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml # Upload build artifacts - name: Upload build artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: smack-java-${{ matrix.java }} path: | diff --git a/.gitignore b/.gitignore index 7bd5f61be..ec8037f60 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ .project .settings .gradle -gradle.properties build/ core/build/ diff --git a/CHANGELOG.md b/CHANGELOG.md index d896bc4e4..b413e7f75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Smack Changelog +# 4.4.8 -- 2024-04-02 + +### Improvement + +[SMACK-941](https://igniterealtime.atlassian.net/browse/SMACK-941) Suppress "roster not loaded while processing presence" warning if its caused by the reflected self-presence + +### Bug + +[SMACK-938](https://igniterealtime.atlassian.net/browse/SMACK-938) Busy loop in SmackReactor + +[SMACK-940](https://igniterealtime.atlassian.net/browse/SMACK-940) Ignore IPv6 Zone IDs in incoming streamhost candidates + # 4.4.7 -- 2023-11-25 ### Improvement diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..4689ef8af --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +GRADLE ?= ./gradlew + +.PHONY: all +all: check jacocoRootReport javadocAll sinttest + +.PHONY: codecov +codecov: + $(GRADLE) smack-java11-full:testCodeCoverageReport + echo "Report available at smack-java11-full/build/reports/jacoco/testCodeCoverageReport/html/index.html" + +.PHONY: check +check: + $(GRADLE) $@ + +.PHONY: eclipse +eclipse: + $(GRADLE) $@ + +.PHONY: sinttest +sinttest: + $(GRADLE) $@ + +.PHONY: jacocoRootReport +jacocoRootReport: + $(GRADLE) $@ + +.PHONY: javadocAll +javadocAll: + $(GRADLE) $@ + echo "Smack javadoc available at build/javadoc/index.html" diff --git a/build-logic/build.gradle b/build-logic/build.gradle new file mode 100644 index 000000000..7b02d03a7 --- /dev/null +++ b/build-logic/build.gradle @@ -0,0 +1,14 @@ +plugins { + id 'groovy-gradle-plugin' +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation "biz.aQute.bnd:biz.aQute.bnd.gradle:7.0.0" + implementation "me.champeau.jmh:jmh-gradle-plugin:0.7.2" + implementation "net.ltgt.gradle:gradle-errorprone-plugin:4.0.1" + implementation "ru.vyarus:gradle-animalsniffer-plugin:1.7.1" +} diff --git a/build-logic/settings.gradle b/build-logic/settings.gradle new file mode 100644 index 000000000..d082ce7e0 --- /dev/null +++ b/build-logic/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'smack-build-logic' diff --git a/build-logic/src/main/groovy/org.igniterealtime.smack.android-boot-classpath-conventions.gradle b/build-logic/src/main/groovy/org.igniterealtime.smack.android-boot-classpath-conventions.gradle new file mode 100644 index 000000000..c14cb3199 --- /dev/null +++ b/build-logic/src/main/groovy/org.igniterealtime.smack.android-boot-classpath-conventions.gradle @@ -0,0 +1,6 @@ +compileJava { + options.bootstrapClasspath = files(androidBootClasspath) +} +javadoc { + classpath += files(androidBootClasspath) +} diff --git a/build-logic/src/main/groovy/org.igniterealtime.smack.android-conventions.gradle b/build-logic/src/main/groovy/org.igniterealtime.smack.android-conventions.gradle new file mode 100644 index 000000000..94beb432c --- /dev/null +++ b/build-logic/src/main/groovy/org.igniterealtime.smack.android-conventions.gradle @@ -0,0 +1,10 @@ +plugins { + id 'ru.vyarus.animalsniffer' + id 'org.igniterealtime.smack.global-conventions' +} +dependencies { + signature "net.sf.androidscents.signature:android-api-level-${smackMinAndroidSdk}:6.0_r3@signature" +} +animalsniffer { + sourceSets = [sourceSets.main] +} diff --git a/build-logic/src/main/groovy/org.igniterealtime.smack.application-conventions.gradle b/build-logic/src/main/groovy/org.igniterealtime.smack.application-conventions.gradle new file mode 100644 index 000000000..fa4c7011e --- /dev/null +++ b/build-logic/src/main/groovy/org.igniterealtime.smack.application-conventions.gradle @@ -0,0 +1,12 @@ +plugins { + id 'application' +} + +application { + applicationDefaultJvmArgs = ["-enableassertions"] +} + +run { + // Pass all system properties down to the "application" run + systemProperties System.getProperties() +} diff --git a/build-logic/src/main/groovy/org.igniterealtime.smack.global-conventions.gradle b/build-logic/src/main/groovy/org.igniterealtime.smack.global-conventions.gradle new file mode 100644 index 000000000..5bd92c7ee --- /dev/null +++ b/build-logic/src/main/groovy/org.igniterealtime.smack.global-conventions.gradle @@ -0,0 +1,37 @@ +ext { + javaVersion = JavaVersion.VERSION_11 + javaMajor = javaVersion.getMajorVersion() + smackMinAndroidSdk = 23 + + androidBootClasspath = { getAndroidRuntimeJar() } +} + +repositories { + mavenLocal() + mavenCentral() +} + +def getAndroidRuntimeJar() { + def androidApiLevel = ext.smackMinAndroidSdk + def androidHome = getAndroidHome() + def androidJar = new File("$androidHome/platforms/android-${androidApiLevel}/android.jar") + if (androidJar.isFile()) { + return androidJar + } else { + throw new Exception("Can't find android.jar for API level ${androidApiLevel}. Please install corresponding SDK platform package") + } +} +def getAndroidJavadocOffline() { + def androidHome = getAndroidHome() + return androidHome.toString() + "/docs/reference" +} + +def getAndroidHome() { + def androidHomeEnv = System.getenv("ANDROID_HOME") + if (androidHomeEnv == null) { + throw new Exception("ANDROID_HOME environment variable is not set") + } + def androidHome = new File(androidHomeEnv) + if (!androidHome.isDirectory()) throw new Exception("Environment variable ANDROID_HOME is not pointing to a directory") + return androidHome +} diff --git a/build-logic/src/main/groovy/org.igniterealtime.smack.java-common-conventions.gradle b/build-logic/src/main/groovy/org.igniterealtime.smack.java-common-conventions.gradle new file mode 100644 index 000000000..598fdfa67 --- /dev/null +++ b/build-logic/src/main/groovy/org.igniterealtime.smack.java-common-conventions.gradle @@ -0,0 +1,367 @@ +plugins { + id 'biz.aQute.bnd.builder' + id 'checkstyle' + id 'eclipse' + id 'idea' + id 'jacoco' + id 'java' + id 'java-library' + id 'java-test-fixtures' + id 'maven-publish' + id 'net.ltgt.errorprone' + id 'signing' + + id 'jacoco-report-aggregation' + id 'test-report-aggregation' + + id 'org.igniterealtime.smack.global-conventions' + id 'org.igniterealtime.smack.javadoc-conventions' +} + +version readVersionFile() + +ext { + isSnapshot = version.endsWith('-SNAPSHOT') + gitCommit = getGitCommit() + rootConfigDir = new File(rootDir, 'config') + sonatypeCredentialsAvailable = project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePassword') + isReleaseVersion = !isSnapshot + isContinuousIntegrationEnvironment = Boolean.parseBoolean(System.getenv('CI')) + signingRequired = !(isSnapshot || isContinuousIntegrationEnvironment) + sonatypeSnapshotUrl = 'https://oss.sonatype.org/content/repositories/snapshots' + sonatypeStagingUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2' + builtDate = (new java.text.SimpleDateFormat("yyyy-MM-dd")).format(new Date()) + oneLineDesc = 'An Open Source XMPP (Jabber) client library' + + jxmppVersion = '[1.1.0-beta1, 1.1.999]' + miniDnsVersion = '[1.1.0-alpha3, 1.1.999]' + junitVersion = '5.9.2' + commonsIoVersion = '2.6' + bouncyCastleVersion = '1.73' + guavaVersion = '30.1-jre' + mockitoVersion = '5.13.0' + orgReflectionsVersion = '0.9.11' + + if (project.hasProperty("useSonatype")) { + useSonatype = project.getProperty("useSonatype").toBoolean() + } else { + // Default to true + useSonatype = true + } + + gplLicensedProjects = [ + ':smack-examples', + ':smack-omemo-signal', + ':smack-omemo-signal-integration-test', + ':smack-repl' + ].collect{ project(it) } +} + +group = 'org.igniterealtime.smack' + +java { + sourceCompatibility = javaVersion + targetCompatibility = sourceCompatibility +} + +eclipse { + classpath { + downloadJavadoc = true + } +} + +// Make all project's 'test' target depend on javadoc, so that +// javadoc is also linted. +test.dependsOn javadoc + +tasks.withType(JavaCompile) { + // Some systems may not have set their platform default + // converter to 'utf8', but we use unicode in our source + // files. Therefore ensure that javac uses unicode + options.encoding = "utf8" + options.compilerArgs = [ + '-Xlint:all', + // Set '-options' because a non-java7 javac will emit a + // warning if source/target is set to 1.7 and + // bootclasspath is *not* set. + '-Xlint:-options', + // TODO: Enable xlint serial + '-Xlint:-serial', + '-Werror', + ] +} +if (JavaVersion.current().isJava8Compatible()) { + tasks.withType(Javadoc) { + // The '-quiet' as second argument is actually a hack, + // since the one parameter addStringOption doesn't seem to + // work, we extra add '-quiet', which is added anyway by + // gradle. + // We disable 'missing' as we do most of javadoc checking via checkstyle. + options.addStringOption('Xdoclint:all,-missing', '-quiet') + // Abort on javadoc warnings. + // See JDK-8200363 (https://bugs.openjdk.java.net/browse/JDK-8200363) + // for information about the -Xwerror option. + options.addStringOption('Xwerror', '-quiet') + } +} + +if (JavaVersion.current().isJava9Compatible()) { + tasks.withType(JavaCompile) { + options.compilerArgs.addAll([ + '--release', javaMajor, + ]) + } +} + +jacoco { + toolVersion = "0.8.12" +} + +jacocoTestReport { + dependsOn test + reports { + xml.required = true + } +} + +dependencies { + testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" + + testFixturesApi "org.junit.jupiter:junit-jupiter-api:$junitVersion" + testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" + // https://stackoverflow.com/a/77274251/194894 + testRuntimeOnly "org.junit.platform:junit-platform-launcher:1.11.0" + + // The smack-extensions subproject uses mockito in its fest + // fixtures, and we want to have mockito also available in + // test, so we use API here. + testFixturesApi "org.mockito:mockito-core:${mockitoVersion}" + + testImplementation 'com.jamesmurty.utils:java-xmlbuilder:1.2' + + errorprone 'com.google.errorprone:error_prone_core:2.32.0' +} + +test { + useJUnitPlatform() + + maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 + + // Enable full stacktraces of failed tests. Especially handy + // for CI environments. + testLogging { + events "failed" + exceptionFormat "full" + } +} + +jar { + manifest { + attributes( + 'Implementation-Version': version, + 'Implementation-GitRevision': gitCommit, + 'Built-JDK': System.getProperty('java.version'), + 'Built-Gradle': gradle.gradleVersion, + 'Built-By': System.getProperty('user.name') + ) + } + + bundle { + bnd( + '-removeheaders': 'Tool, Bnd-*', + '-exportcontents': '*', + ) + } +} + +checkstyle { + toolVersion = '8.27' + + if (project in gplLicensedProjects) { + configProperties.checkstyleLicenseHeader = "${project.name}-gplv3-license-header" + } else { + configProperties.checkstyleLicenseHeader = "header" + } +} +task sourcesJar(type: Jar, dependsOn: classes) { + archiveClassifier = 'sources' + from sourceSets.main.allSource +} +task javadocJar(type: Jar, dependsOn: javadoc) { + archiveClassifier = 'javadoc' + from javadoc.destinationDir +} +task testsJar(type: Jar) { + archiveClassifier = 'tests' + from sourceSets.test.output +} +configurations { + testRuntime +} +artifacts { + // Add a 'testRuntime' configuration including the tests so that + // it can be consumed by other projects (smack-omemo-signal for + // example). See http://stackoverflow.com/a/21946676/194894 + testRuntime testsJar +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + artifact sourcesJar + artifact javadocJar + artifact testsJar + pom { + name = 'Smack' + packaging = 'jar' + inceptionYear = '2003' + url = 'http://www.igniterealtime.org/projects/jxmpp/' + afterEvaluate { + description = project.description + } + + issueManagement { + system = 'JIRA' + url = 'http://issues.igniterealtime.org/browse/SMACK' + } + + scm { + url = 'https://github.com/igniterealtime/Smack' + connection = 'scm:git:https://github.com/igniterealtime/Smack.git' + developerConnection = 'scm:git:https://github.com/igniterealtime/Smack.git' + } + + licenses { + if (project in gplLicensedProjects) { + license { + name = 'GNU General Public License, version 3 or any later version' + url = 'https://www.gnu.org/licenses/gpl.txt' + distribution = 'repo' + } + } else { + license { + name = 'The Apache Software License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + distribution = 'repo' + } + } + } + + developers { + developer { + id = 'flow' + name = 'Florian Schmaus' + email = 'flow@igniterealtime.org' + } + } + } + } + } + repositories { + if (sonatypeCredentialsAvailable && useSonatype) { + maven { + url isSnapshot ? sonatypeSnapshotUrl : sonatypeStagingUrl + credentials { + username = sonatypeUsername + password = sonatypePassword + } + } + } + // Use + // gradle publish -P customRepoUrl=https://www.igniterealtime.org/archiva/repository/maven -P customRepoUsername=bamboo -P customRepoPassword=hidden -P useSonatype=false + // to deploy to this repo. + if (project.hasProperty("customRepoUrl")) { + maven { + name 'customRepo' + url customRepoUrl + if (project.hasProperty("customRepoUsername")) { + credentials { + username customRepoUsername + password customRepoPassword + } + } + } + } + } +} + +// Workaround for gpg signatory not supporting the 'required' option +// See https://github.com/gradle/gradle/issues/5064#issuecomment-381924984 +// Note what we use 'signing.gnupg.keyName' instead of 'signing.keyId'. +tasks.withType(Sign) { + onlyIf { + project.hasProperty('signing.gnupg.keyName') + } +} +signing { + required { signingRequired } + useGpgCmd() + sign publishing.publications.mavenJava +} + +tasks.withType(JavaCompile) { + options.errorprone { + disableWarningsInGeneratedCode = true + excludedPaths = ".*/jmh_generated/.*" + error( + "UnusedVariable", + "UnusedMethod", + "MethodCanBeStatic", + ) + errorproneArgs = [ + // Disable MissingCasesInEnumSwitch error prone check + // because this check is already done by javac as incomplete-switch. + '-Xep:MissingCasesInEnumSwitch:OFF', + '-Xep:StringSplitter:OFF', + '-Xep:JavaTimeDefaultTimeZone:OFF', + '-Xep:InlineMeSuggester:OFF', + ] + } +} + +// Work around https://github.com/gradle/gradle/issues/4046 +task copyJavadocDocFiles(type: Copy) { + from('src/javadoc') + into 'build/docs/javadoc' + include '**/doc-files/*.*' +} +javadoc.dependsOn copyJavadocDocFiles + +def getGitCommit() { + def projectDirFile = new File("$projectDir") + + def cmd = 'git describe --always --tags --dirty=+' + def proc = cmd.execute(null, projectDirFile) + + def exitStatus = proc.waitFor() + if (exitStatus != 0) return "non-git build" + + def gitCommit = proc.text.trim() + assert !gitCommit.isEmpty() + gitCommit +} + +def getAndroidRuntimeJar() { + def androidHome = new File("$System.env.ANDROID_HOME") + if (!androidHome.isDirectory()) throw new Exception("ANDROID_HOME not found or set") + def androidJar = new File("$androidHome/platforms/android-$smackMinAndroidSdk/android.jar") + if (androidJar.isFile()) { + return androidJar + } else { + throw new Exception("Can't find android.jar for $smackMinAndroidSdk API. Please install corresponding SDK platform package") + } +} + +def readVersionFile() { + def versionFile = new File(rootDir, 'version') + if (!versionFile.isFile()) { + throw new Exception("Could not find version file") + } + if (versionFile.text.isEmpty()) { + throw new Exception("Version file does not contain a version") + } + versionFile.text.trim() +} diff --git a/build-logic/src/main/groovy/org.igniterealtime.smack.javadoc-conventions.gradle b/build-logic/src/main/groovy/org.igniterealtime.smack.javadoc-conventions.gradle new file mode 100644 index 000000000..61cb8f3c6 --- /dev/null +++ b/build-logic/src/main/groovy/org.igniterealtime.smack.javadoc-conventions.gradle @@ -0,0 +1,30 @@ +plugins { + // Javadoc linking requires repositories to bet configured. And + // those are declared in global-conventions, hence we add it here. + id 'org.igniterealtime.smack.global-conventions' +} + +if (JavaVersion.current().isJava8Compatible()) { + tasks.withType(Javadoc) { + // The '-quiet' as second argument is actually a hack, + // since the one parameter addStringOption doesn't seem to + // work, we extra add '-quiet', which is added anyway by + // gradle. + // We disable 'missing' as we do most of javadoc checking via checkstyle. + options.addStringOption('Xdoclint:all,-missing', '-quiet') + // Abort on javadoc warnings. + // See JDK-8200363 (https://bugs.openjdk.java.net/browse/JDK-8200363) + // for information about the -Xwerror option. + options.addStringOption('Xwerror', '-quiet') + } +} + +if (JavaVersion.current().isJava9Compatible()) { + tasks.withType(Javadoc) { + options.addStringOption('-release', javaMajor) + } +} + +tasks.withType(Javadoc) { + options.charSet = "UTF-8" +} diff --git a/build.gradle b/build.gradle index be40f20fe..13f9b12d7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,349 +1,20 @@ -buildscript { - repositories { - jcenter() - maven { url 'https://plugins.gradle.org/m2/' } - maven { url 'https://dl.bintray.com/content/aalmiray/kordamp' } - } - dependencies { - classpath 'org.kordamp.gradle:clirr-gradle-plugin:0.2.2' - classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:6.0.0" - } -} - plugins { - id 'ru.vyarus.animalsniffer' version '1.5.0' - id 'net.ltgt.errorprone' version '1.3.0' - // Use e.g. "gradle taskTree" to show its dependency tree. - id 'com.dorongold.task-tree' version '1.5' - id 'com.github.kt3k.coveralls' version '2.10.2' + // The scalastyle plugin of smack-repl wants the root project to + // have a ideaProject task, so let's add one. + id 'idea' + + id 'org.igniterealtime.smack.javadoc-conventions' } ext { - java11Projects = [ - ':smack-examples', + javadocAllDir = new File(buildDir, 'javadoc') + integrationTestProjects = [ ':smack-integration-test', ':smack-omemo-signal-integration-test', - ':smack-repl', - ':smack-websocket-java11', - ].collect { project(it) } - java11Projects += getRootProject() - java8Projects = allprojects - java11Projects + ].collect{ project(it) } + javadocAllProjects = subprojects - integrationTestProjects } -configure (java8Projects) { - ext { - javaCompatilibity = JavaVersion.VERSION_1_8 - } -} - -configure (java11Projects) { - ext { - javaCompatilibity = JavaVersion.VERSION_11 - } -} - -allprojects { - apply plugin: 'java-library' - apply plugin: 'java-test-fixtures' - apply plugin: 'eclipse' - apply plugin: 'idea' - apply plugin: 'jacoco' - apply plugin: 'net.ltgt.errorprone' - - version readVersionFile() - - ext { - isSnapshot = version.endsWith('-SNAPSHOT') - gitCommit = getGitCommit() - javadocAllDir = new File(buildDir, 'javadoc') - documentationDir = new File(projectDir, 'documentation') - releasedocsDir = new File(buildDir, 'releasedocs') - rootConfigDir = new File(rootDir, 'config') - sonatypeCredentialsAvailable = project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePassword') - isReleaseVersion = !isSnapshot - isContinuousIntegrationEnvironment = Boolean.parseBoolean(System.getenv('CI')) - signingRequired = !(isSnapshot || isContinuousIntegrationEnvironment) - sonatypeSnapshotUrl = 'https://oss.sonatype.org/content/repositories/snapshots' - sonatypeStagingUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2' - // Returns only the date in yyyy-MM-dd format, as otherwise, with - // hh:mm:ss information, the manifest files would change with every - // build, causing unnecessary rebuilds. - builtDate = (new java.text.SimpleDateFormat("yyyy-MM-dd")).format(new Date()) - oneLineDesc = 'An Open Source XMPP (Jabber) client library' - integrationTestProjects = [ - ':smack-integration-test', - ':smack-omemo-signal-integration-test', - ].collect{ project(it) } - javadocAllProjects = subprojects - integrationTestProjects - // A dirty hack used for Gradle's jacoco plugin, since is not - // hable to handle the case when a (sub)project has no unit - // tests. :-( - projectsWithoutUnitTests = [ - ':smack-android', - ':smack-android-extensions', - ':smack-bosh', - ':smack-debug', - ':smack-debug-slf4j', - ':smack-java8', - ':smack-jingle-old', - ':smack-resolver-dnsjava', - ':smack-resolver-javax', - ':smack-resolver-minidns', - ':smack-omemo-signal-integration-test', - ].collect{ project(it) } - projectsWithUnitTests = subprojects - projectsWithoutUnitTests - androidProjects = [ - ':smack-tcp', - ':smack-bosh', - ':smack-core', - ':smack-im', - ':smack-resolver-minidns', - ':smack-sasl-provided', - ':smack-extensions', - ':smack-experimental', - ':smack-omemo', - ':smack-omemo-signal', - ':smack-openpgp', - ':smack-xmlparser', - ':smack-xmlparser-xpp3', - ].collect{ project(it) } - androidBootClasspathProjects = [ - ':smack-android', - ':smack-android-extensions', - ].collect{ project(it) } - androidOptionalProjects = [ - ':smack-tcp', - ':smack-extensions', - ':smack-experimental', - ':smack-bosh', - ':smack-omemo', - ':smack-omemo-signal', - ].collect{ project(it) } - gplLicensedProjects = [ - ':smack-examples', - ':smack-omemo-signal', - ':smack-omemo-signal-integration-test', - ':smack-repl' - ].collect{ project(it) } - // Lazily evaluate the Android bootClasspath and offline - // Javadoc using a closure, so that targets which do not - // require it are still able to succeed without an Android - // SDK. - androidBootClasspath = { getAndroidRuntimeJar() } - androidJavadocOffline = { getAndroidJavadocOffline() } - junit4Projects = [ - ':smack-core', - ':smack-im', - ':smack-omemo', - ':smack-omemo-signal', - ':smack-openpgp', - ].collect { project(it) } - - // When using dynamic versions for those, do *not* use [1.0, - // 2.0), since this will also pull in 2.0-alpha1. Instead use - // [1.0, 1.0.99]. - // See also: - // - https://issues.apache.org/jira/browse/MNG-6232 - // - https://issues.igniterealtime.org/browse/SMACK-858 - jxmppVersion = '[1.0.0, 1.0.999]' - miniDnsVersion = '[1.0.0, 1.0.999]' - smackMinAndroidSdk = 19 - junitVersion = '5.7.1' - commonsIoVersion = '2.6' - bouncyCastleVersion = '1.73' - guavaVersion = '30.1-jre' - mockitoVersion = '3.7.7' - orgReflectionsVersion = '0.9.11' - - if (project.hasProperty("useSonatype")) { - useSonatype = project.getProperty("useSonatype").toBoolean() - } else { - // Default to true - useSonatype = true - } - javaMajor = javaCompatilibity.getMajorVersion() - } - group = 'org.igniterealtime.smack' - sourceCompatibility = javaCompatilibity - targetCompatibility = sourceCompatibility - - test { - useJUnitPlatform() - - maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 - - // Enable full stacktraces of failed tests. Especially handy - // for environments like Travis. - testLogging { - events "failed" - exceptionFormat "full" - } - } - - ext.sharedManifest = manifest { - attributes('Implementation-Version': version, - 'Implementation-GitRevision': ext.gitCommit, - 'Built-Date': ext.builtDate, - 'Built-JDK': System.getProperty('java.version'), - 'Built-Gradle': gradle.gradleVersion, - 'Built-By': System.getProperty('user.name') - ) - } - - eclipse { - classpath { - downloadJavadoc = true - } - } - - repositories { - mavenLocal() - mavenCentral() - } - - tasks.withType(JavaCompile) { - // Some systems may not have set their platform default - // converter to 'utf8', but we use unicode in our source - // files. Therefore ensure that javac uses unicode - options.encoding = 'UTF-8' - options.compilerArgs = [ - '-Xlint:all', - // Set '-options' because a non-java7 javac will emit a - // warning if source/traget is set to 1.7 and - // bootclasspath is *not* set. - // TODO implement a sound heuristic to determine a java7 - // rt.jar on the build host. And if none is found, - // fallback to using a environment variable, - // e.g. JAVA7_HOME. See SMACK-651. - '-Xlint:-options', - '-Werror', - ] - options.errorprone { - error( - "UnusedVariable", - "UnusedMethod", - "MethodCanBeStatic", - ) - errorproneArgs = [ - // Disable errorprone checks - '-Xep:TypeParameterUnusedInFormals:OFF', - // Disable errorpone StringSplitter check, as it - // recommends using Splitter from Guava, which we don't - // have (nor want to use in Smack). - '-Xep:StringSplitter:OFF', - '-Xep:JdkObsolete:OFF', - // Disabled because sinttest re-uses BeforeClass from junit. - // TODO: change sinttest so that it has it's own - // BeforeClass and re-enable this check. - '-Xep:JUnit4ClassAnnotationNonStatic:OFF', - // Disabled but should be re-enabled at some point - //'-Xep:InconsistentCapitalization:OFF', - '-Xep:MixedMutabilityReturnType:OFF', - // TODO: Re-enable once Smack's minimum Android SDK level is 26 or higher. - '-Xep:JavaUtilDate:OFF', - ] - } - } - - tasks.withType(ScalaCompile) { - scalaCompileOptions.additionalParameters = [ - '-Xfatal-warnings', - '-feature', - ] - } - - jacoco { - toolVersion = "0.8.6" - } - - jacocoTestReport { - dependsOn test - getSourceDirectories().setFrom(project.files(sourceSets.main.allSource.srcDirs)) - getClassDirectories().setFrom(project.files(sourceSets.main.output)) - reports { - xml.enabled true - } - } - - if (JavaVersion.current().isJava8Compatible()) { - tasks.withType(Javadoc) { - // The '-quiet' as second argument is actually a hack, - // since the one paramater addStringOption doesn't seem to - // work, we extra add '-quiet', which is added anyway by - // gradle. - // TODO: This enables all doclint check but - // 'missing'. Re-enable 'missing' once every public API in - // Smack has a javadoc comment. - options.addStringOption('Xdoclint:accessibility,html,reference,syntax', '-quiet') - - // Treat warnings as errors. - // See also https://bugs.openjdk.java.net/browse/JDK-8200363 - options.addStringOption('Xwerror', '-quiet') - } - } - - if (JavaVersion.current().isJava9Compatible()) { - tasks.withType(Javadoc) { - options.addStringOption('-release', javaMajor) - - // The -no-modules-directories javadoc option was removed in Java 13 - // See https://bugs.openjdk.java.net/browse/JDK-8215582 - if (JavaVersion.current() < JavaVersion.VERSION_13) { - // Fix for javadoc search. If not set, the search result would direct to - // javadoc/undefined/org/jivesoftware/smack/altconnections/HttpLookupMethod.html - // instead of - // javadoc/org/jivesoftware/smack/altconnections/HttpLookupMethod.html - // https://stackoverflow.com/a/53732633/194894 - options.addBooleanOption("-no-module-directories", true) - } - } - tasks.withType(JavaCompile) { - options.compilerArgs.addAll([ - '--release', javaMajor, - ]) - } - } - -tasks.withType(Javadoc) { - options.charSet = "UTF-8" - options.encoding = 'UTF-8' - } - - dependencies { - testFixturesApi "org.junit.jupiter:junit-jupiter-api:$junitVersion" - testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion" - testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" - - // The smack-extensions subproject uses mockito in its fest - // fixtures, and we want to have mockito also available in - // test, so we use API here. - testFixturesApi "org.mockito:mockito-core:${mockitoVersion}" - - // To mock final classes - testImplementation "org.mockito:mockito-inline:${mockitoVersion}" - testImplementation 'com.jamesmurty.utils:java-xmlbuilder:1.2' - - errorprone 'com.google.errorprone:error_prone_core:2.5.1' - errorproneJavac('com.google.errorprone:javac:9+181-r4173-1') - } - - // Make all project's 'test' target depend on javadoc, so that - // javadoc is also linted. - test { dependsOn javadoc } -} - -configure (junit4Projects) { - dependencies { - testImplementation "org.junit.vintage:junit-vintage-engine:$junitVersion" - } -} - -// We need to evaluate the child projects first because -// - javadocAll needs the smack-core child to have already resolved -// the jXMPP/MiniDNS dependencies, so that we can the resovled -// version to link to those project's javadoc. -// - We use the child's project description as description for the -// Maven POM. evaluationDependsOnChildren() task javadocAll(type: Javadoc) { source javadocAllProjects.collect {project -> @@ -392,315 +63,6 @@ task javadocAll(type: Javadoc) { } } -import org.apache.tools.ant.filters.ReplaceTokens -task prepareReleasedocs(type: Copy) { - from 'resources/releasedocs' - into releasedocsDir - filter(ReplaceTokens, tokens: [version: version, releasedate: builtDate, targetCompatibility: targetCompatibility.toString()]) -} - -task distributionZip(type: Zip, dependsOn: [javadocAll, prepareReleasedocs]) { - classifier builtDate - into ('javadoc') { - from(javadocAllDir) - } - into ('releasedocs') { - from(releasedocsDir) - } - into ('releasedocs/documentation') { - from(documentationDir) - } -} - -task maybeCheckForSnapshotDependencies { - // Don't check for Snapshot dependencies if this is a snapshot. - onlyIf { isReleaseVersion } - // Run in the execution phase, not in configuration phase, as the - // 'each' forces the runtime configuration to be resovled, which - // causes "Cannot change dependencies of configuration after it - // has been included in dependency resolution." errors. - // See https://discuss.gradle.org/t/23153 - doLast { - allprojects { project -> - project.configurations.runtime.each { - if (it.toString().contains("-SNAPSHOT")) - throw new Exception("Release build contains snapshot dependencies: " + it) - } - } - } -} - -test { dependsOn maybeCheckForSnapshotDependencies } - -jar { - // Root project should not create empty jar artifact - enabled = false -} - -// Disable upload archives for the root project -uploadArchives.enabled = false - -description = """\ -Smack ${version} -${oneLineDesc}.""" - -subprojects { - apply plugin: 'maven-publish' - apply plugin: 'signing' - apply plugin: 'checkstyle' - apply plugin: 'org.kordamp.gradle.clirr' - apply plugin: 'biz.aQute.bnd.builder' - - checkstyle { - toolVersion = '8.27' - } - task sourcesJar(type: Jar, dependsOn: classes) { - classifier = 'sources' - from sourceSets.main.allSource - } - task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir - } - task testsJar(type: Jar, dependsOn: testClasses) { - classifier = 'tests' - from sourceSets.test.output - } - - artifacts { - // See http://stackoverflow.com/a/21946676/194894 - testRuntime testsJar - } - - publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - artifact testsJar - pom { - name = 'Smack' - packaging = 'jar' - inceptionYear = '2003' - url = 'http://www.igniterealtime.org/projects/smack/' - description = project.description - - issueManagement { - system = 'JIRA' - url = 'https://igniterealtime.org/issues/browse/SMACK' - } - - scm { - url = 'https://github.com/igniterealtime/Smack' - connection = 'scm:git:https://github.com/igniterealtime/Smack.git' - developerConnection = 'scm:git:https://github.com/igniterealtime/Smack.git' - } - - developers { - developer { - id = 'flow' - name = 'Florian Schmaus' - email = 'flow@igniterealtime.org' - } - } - } - } - } - repositories { - if (sonatypeCredentialsAvailable && useSonatype) { - maven { - url isSnapshot ? sonatypeSnapshotUrl : sonatypeStagingUrl - credentials { - username = sonatypeUsername - password = sonatypePassword - } - } - } - // Use - // gradle publish -P customRepoUrl=https://www.igniterealtime.org/archiva/repository/maven -P customRepoUsername=bamboo -P customRepoPassword=hidden -P useSonatype=false - // to deploy to this repo. - if (project.hasProperty("customRepoUrl")) { - maven { - name 'customRepo' - url customRepoUrl - if (project.hasProperty("customRepoUsername")) { - credentials { - username customRepoUsername - password customRepoPassword - } - } - } - } - } - } - rootProject.distributionZip { - dependsOn build - from(buildDir) { - include "$libsDirName/*${version}.jar" - include "$libsDirName/*${version}-javadoc.jar" - include "$libsDirName/*${version}-sources.jar" - } - } - - // Workaround for gpg signatory not supporting the 'required' option - // See https://github.com/gradle/gradle/issues/5064#issuecomment-381924984 - // Note what we use 'signing.gnupg.keyName' instead of 'signing.keyId'. - tasks.withType(Sign) { - onlyIf { - project.hasProperty('signing.gnupg.keyName') - } - } - signing { - useGpgCmd() - required { signingRequired } - sign publishing.publications.mavenJava - } - - clirr { - // 2018-08-14: Disabled Clirr because - // - It reports an breaking change in android.jar (seems right, but there is nothing we can do about it) - // - Only the first smack-* projects are correctly checked, - // the other ones have the output of a clirr report from a previous project - // (Look at the clirr reports). - enabled false - semver false - } - - // Work around https://github.com/gradle/gradle/issues/4046 - task copyJavadocDocFiles(type: Copy) { - from('src/javadoc') - into 'build/docs/javadoc' - include '**/doc-files/*.*' - } - javadoc.dependsOn copyJavadocDocFiles - - // Make sure root projects 'javadocAll' depends on the - // subproject's javadoc, to ensure that all all doc-files/ are - // generated and up-to-date. Obviously this means that the - // javadocAll task will also create the individual javadoc's of the - // subprojects. - javadocAll.dependsOn javadoc -} - -// The smack-java8-full project generates the dot and png files of the -// current state graph. Ensure they are generated before copied. -configure (project(':smack-java8-full')) { - copyJavadocDocFiles.dependsOn convertModularXmppClientToServerConnectionStateGraphDotToPng -} - -configure (androidProjects + androidBootClasspathProjects) { - apply plugin: 'ru.vyarus.animalsniffer' - dependencies { - signature "net.sf.androidscents.signature:android-api-level-${smackMinAndroidSdk}:4.4.2_r4@signature" - } - animalsniffer { - sourceSets = [sourceSets.main] - } -} - -// There is no need to ever clirr integration test projects and the -// smack-repl project. -configure(integrationTestProjects + project(':smack-repl')) { - clirr { - enabled false - } -} - -// Disable clirr on omemo modules -project(':smack-omemo').clirr.enabled = false -project(':smack-omemo-signal').clirr.enabled = false - -subprojects*.jar { - manifest { - from sharedManifest - } - bundle { - bnd( - '-removeheaders': 'Tool, Bnd-*', - '-exportcontents': '*', - ) - } -} - -configure(subprojects - gplLicensedProjects) { - checkstyle { - configProperties.checkstyleLicenseHeader = "header" - } - publishing { - publications { - mavenJava(MavenPublication) { - pom { - licenses { - license { - name = 'The Apache Software License, Version 2.0' - url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' - distribution = 'repo' - } - } - } - } - } - } -} - -configure(gplLicensedProjects) { - checkstyle { - configProperties.checkstyleLicenseHeader = "${project.name}-gplv3-license-header" - } - publishing { - publications { - mavenJava(MavenPublication) { - pom { - licenses { - license { - name = 'GNU General Public License, version 3 or any later version' - url = 'https://www.gnu.org/licenses/gpl.txt' - distribution = 'repo' - } - } - } - } - } - } -} - -configure(androidBootClasspathProjects) { - compileJava { - options.bootstrapClasspath = files(androidBootClasspath) - } - javadoc { - classpath += files(androidBootClasspath) - } -} - -apply plugin: "com.github.kt3k.coveralls" -coveralls { - sourceDirs = files(subprojects.sourceSets.main.allSource.srcDirs).files.absolutePath -} - -task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) { - dependsOn = projectsWithUnitTests.jacocoTestReport - getSourceDirectories().setFrom(files(projectsWithUnitTests.sourceSets.main.allSource.srcDirs)) - getClassDirectories().setFrom(files(projectsWithUnitTests.sourceSets.main.output)) - getExecutionData().setFrom(files(projectsWithUnitTests.jacocoTestReport.executionData)) - reports { - xml.enabled true - xml.destination file("${buildDir}/reports/jacoco/test/jacocoTestReport.xml") - } - // We could remove the following setOnlyIf line, but then - // jacocoRootReport would silently be SKIPPED if something with - // the projectsWithUnitTests is wrong (e.g. a project is missing - // in there). - setOnlyIf { true } -} - -// Important to specify this task after the subprojects block -task clirrRootReport(type: org.kordamp.gradle.clirr.ClirrReportTask) { - dependsOn = subprojects.tasks.clirr - reports = files((subprojects.findAll { it.clirr.enabled == true }).tasks.clirr.xmlReport) -} - task integrationTest { description 'Verify correct functionality of Smack by running some integration tests.' dependsOn project(':smack-integration-test').tasks.run @@ -708,7 +70,7 @@ task integrationTest { task omemoSignalIntTest { description 'Run integration tests of the smack-omemo module in combination with smack-omemo-signal.' - dependsOn project(':smack-omemo-signal-integration-test').tasks.run + dependsOn 'smack-omemo-signal-integration-test:run' } task sinttestAll { @@ -719,70 +81,6 @@ task sinttestAll { ]} } -def getGitCommit() { - def projectDirFile = new File("$projectDir") - def dotGit = new File(projectDirFile, ".git") - if (!dotGit.isDirectory()) return 'non-git build' - - def cmd = 'git describe --always --tags --dirty=+' - def proc = cmd.execute(null, projectDirFile) - proc.waitForOrKill(10 * 1000) - - def gitCommit = proc.text.trim() - assert !gitCommit.isEmpty() - - def srCmd = 'git symbolic-ref --short HEAD' - def srProc = srCmd.execute(null, projectDirFile) - srProc.waitForOrKill(10 * 1000) - if (srProc.exitValue() == 0) { - // Only add the information if the git command was - // successful. There may be no symbolic reference for HEAD if - // e.g. in detached mode. - def symbolicReference = srProc.text.trim() - assert !symbolicReference.isEmpty() - gitCommit += "-$symbolicReference" - } - - gitCommit -} - -def getAndroidRuntimeJar() { - def androidApiLevel = ext.smackMinAndroidSdk - def androidHome = getAndroidHome() - def androidJar = new File("$androidHome/platforms/android-${androidApiLevel}/android.jar") - if (androidJar.isFile()) { - return androidJar - } else { - throw new Exception("Can't find android.jar for API level ${androidApiLevel}. Please install corresponding SDK platform package") - } -} - -def getAndroidJavadocOffline() { - def androidHome = getAndroidHome() - return androidHome.toString() + "/docs/reference" -} - -def getAndroidHome() { - def androidHomeEnv = System.getenv("ANDROID_HOME") - if (androidHomeEnv == null) { - throw new Exception("ANDROID_HOME environment variable is not set") - } - def androidHome = new File(androidHomeEnv) - if (!androidHome.isDirectory()) throw new Exception("Environment variable ANDROID_HOME is not pointing to a directory") - return androidHome -} - -def readVersionFile() { - def versionFile = new File(rootDir, 'version') - if (!versionFile.isFile()) { - throw new Exception("Could not find version file") - } - if (versionFile.text.isEmpty()) { - throw new Exception("Version file does not contain a version") - } - versionFile.text.trim() -} - def getResolvedVersion(queriedProject = 'smack-core', component) { def configuration = project(queriedProject) .configurations @@ -793,7 +91,7 @@ def getResolvedVersion(queriedProject = 'smack-core', component) { .resolvedArtifacts .findAll { // 'it' is of type ResolvedArtifact, 'id' of - // Component*Artifcat*Identifier, and we check the + // Component*Artifact*Identifier, and we check the // ComponentIdentifier. it.id.getComponentIdentifier() instanceof org.gradle.api.artifacts.component.ModuleComponentIdentifier } diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 000000000..671a138d8 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,5 @@ +# Workaround for https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/349 +# suggested at https://docs.gradle.org/current/userguide/upgrading_version_8.html#xml_parsing_now_requires_recent_parsers +systemProp.javax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl +systemProp.javax.xml.transform.TransformerFactory=com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl +systemProp.javax.xml.parsers.DocumentBuilderFactory=com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c02..a4b76b953 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 442d9132e..df97d72b8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0c8..f5feea6d6 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,69 +15,104 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +122,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,88 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index ac1b06f93..9b42019c7 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/repl b/repl index c6014bbb4..e31926a9d 100755 --- a/repl +++ b/repl @@ -1,7 +1,5 @@ #!/usr/bin/env bash -set -e -set -u -set -o pipefail +set -euo pipefail JDWP=false JDWP_PORT=8000 @@ -37,12 +35,13 @@ echo "Compiling and computing classpath (May take a while)" # /smack/smack-repl/build/classes/main:/smack/smack-repl/build/ # resources/main:/smack/smack-tcp/build/libs/smack-tcp-4.2.0-alpha4-SNAPSHOT.jar # So perform a "tail -n1" on the output of gradle -GRADLE_CLASSPATH="$(gradle :smack-repl:printClasspath --quiet |\ +GRADLE_CLASSPATH="$(${GRADLE_BIN:-./gradlew} :smack-repl:printClasspath --quiet |\ tail -n1)" echo "Finished, starting REPL" -java "${EXTRA_JAVA_ARGS[@]}" \ +exec java \ + "${EXTRA_JAVA_ARGS[@]}" \ -Dscala.usejavacp=true \ -classpath "${GRADLE_CLASSPATH}" \ ammonite.Main \ - --predef "smack-repl/scala.repl" + --predef smack-repl/scala.repl diff --git a/resources/fix-a-javadoc.sh b/resources/fix-a-javadoc.sh index 94e8f31fd..a300a1a59 100755 --- a/resources/fix-a-javadoc.sh +++ b/resources/fix-a-javadoc.sh @@ -29,7 +29,7 @@ SMACK_EXCEPTIONS[SmackException]="if Smack detected an exceptional situation." SMACK_EXCEPTIONS[XMPPException]="if an XMPP protocol error was received." SMACK_EXCEPTIONS[SmackSaslException]="if a SASL specific error occurred." SMACK_EXCEPTIONS[SASLErrorException]="if a SASL protocol error was returned." -SMACK_EXCEPTIONS[NotAMucServiceException]="if the entity is not a MUC serivce." +SMACK_EXCEPTIONS[NotAMucServiceException]="if the entity is not a MUC service." SMACK_EXCEPTIONS[NoSuchAlgorithmException]="if no such algorithm is available." SMACK_EXCEPTIONS[KeyManagementException]="if there was a key mangement error." SMACK_EXCEPTIONS[XmppStringprepException]="if the provided string is invalid." @@ -53,7 +53,7 @@ SMACK_EXCEPTIONS[Exception]="if an exception occurred." SMACK_EXCEPTIONS[TestNotPossibleException]="if the test is not possible." SMACK_EXCEPTIONS[TimeoutException]="if there was a timeout." SMACK_EXCEPTIONS[IllegalStateException]="if an illegal state was encountered" -SMACK_EXCEPTIONS[NoSuchPaddingException]="if the requested padding mechanism is not availble." +SMACK_EXCEPTIONS[NoSuchPaddingException]="if the requested padding mechanism is not available." SMACK_EXCEPTIONS[BadPaddingException]="if the input data is not padded properly." SMACK_EXCEPTIONS[InvalidKeyException]="if the key is invalid." SMACK_EXCEPTIONS[IllegalBlockSizeException]="if the input data length is incorrect." diff --git a/resources/getCopyright.sh b/resources/getCopyright.sh index c34fd87da..fdde88b0f 100755 --- a/resources/getCopyright.sh +++ b/resources/getCopyright.sh @@ -15,7 +15,7 @@ for p in $SUBPROJECTS; do sort | \ # Remove duplicates uniq | \ - # Split multi Copyright statemtents, e.g. "2001-2013 FooBar, 2014 Baz" + # Split multi Copyright statements, e.g. "2001-2013 FooBar, 2014 Baz" tr ',' '\n' | \ # Remove whitespaces resulting from the previous split sed "s/^[ \t]*//" | \ diff --git a/resources/releasedocs/README.html b/resources/releasedocs/README.html deleted file mode 100644 index d25eaf624..000000000 --- a/resources/releasedocs/README.html +++ /dev/null @@ -1,221 +0,0 @@ - - - - - Smack Readme - - - - -
- - - - -
- -

- - - - - - - - - -
version:@version@
released:@releasedate@
- -

-Thank you for downloading Smack! This version of Smack is compatible -with JVMs @targetCompatibility@ or higher. Using a build system which -is able to consume Maven artifacts, like gradle or Maven, is highly -recommended when using Smack. -

- -

- This is not the real README. Please visit -

- https://www.igniterealtime.org/projects/smack/readme -
- for the README of the current stable Smack version. -

- -

- Smack tries to depend on as few as possible libraries. The only - requirement is jXMPP. For DNS - resolution we recommend to - use MiniDNS. -

- -

-Start off by viewing the documentation -that can be found in the "documentation" directory included with this distribution. -

-Further information can be found on the -Smack website. If you need help using or would like to make contributions or -fixes to the code, please visit the -online forum. -

- -

Changelog and Upgrading

- -View the changelog for a list of changes since the -last release. - -

License Agreements

-

    -
  • Use of the Smack source code is governed by the Apache License Version 2.0: -
    - Copyright 2002-2008 Jive Software.
    -
    - Licensed under the Apache License, Version 2.0 (the "License");
    - you may not use this file except in compliance with the License.
    - You may obtain a copy of the License at
    -
    -     http://www.apache.org/licenses/LICENSE-2.0
    -
    - Unless required by applicable law or agreed to in writing, software
    - distributed under the License is distributed on an "AS IS" BASIS,
    - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    - See the License for the specific language governing permissions and
    - limitations under the License.
    - 
  • - -
  • Smack contains icons and images licensed from INCORS GmbH. You are not licensed -to use these icons outside of Smack.
  • - -
  • Third-party source code is licensed as noted in their source files. - -
-
-
- - diff --git a/settings.gradle b/settings.gradle index 8a7fececc..d433b95d3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,7 @@ +pluginManagement { + includeBuild('build-logic') +} + // The name of the root project. // If we would not set the name, then gradle would use the directory // name of the root directory @@ -23,8 +27,8 @@ include 'smack-core', 'smack-bosh', 'smack-android', 'smack-android-extensions', - 'smack-java8', - 'smack-java8-full', + 'smack-java11', + 'smack-java11-full', 'smack-integration-test', 'smack-omemo', 'smack-omemo-signal', diff --git a/smack-android-extensions/build.gradle b/smack-android-extensions/build.gradle index 0cdad8bda..ce9a40fc4 100644 --- a/smack-android-extensions/build.gradle +++ b/smack-android-extensions/build.gradle @@ -1,3 +1,9 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' + id 'org.igniterealtime.smack.android-boot-classpath-conventions' +} + description = """\ Extra Smack extensions for Android.""" @@ -8,5 +14,5 @@ dependencies { api project(':smack-extensions') // Add the Android jar to the Eclipse .classpath. - compileClasspath files(androidBootClasspath) + implementation files(androidBootClasspath) } diff --git a/smack-android-extensions/src/main/java/org/jivesoftware/smackx/package-info.java b/smack-android-extensions/src/main/java/org/jivesoftware/smackx/package-info.java index 7ebe86244..bce3b6064 120000 --- a/smack-android-extensions/src/main/java/org/jivesoftware/smackx/package-info.java +++ b/smack-android-extensions/src/main/java/org/jivesoftware/smackx/package-info.java @@ -1 +1 @@ -../../../../../../../smack-java8-full/src/main/java/org/jivesoftware/smackx/package-info.java \ No newline at end of file +../../../../../../../smack-java11-full/src/main/java/org/jivesoftware/smackx/package-info.java \ No newline at end of file diff --git a/smack-android-extensions/src/main/java/org/jivesoftware/smackx/ping/android/ServerPingWithAlarmManager.java b/smack-android-extensions/src/main/java/org/jivesoftware/smackx/ping/android/ServerPingWithAlarmManager.java index a81f51ade..0315629c8 100644 --- a/smack-android-extensions/src/main/java/org/jivesoftware/smackx/ping/android/ServerPingWithAlarmManager.java +++ b/smack-android-extensions/src/main/java/org/jivesoftware/smackx/ping/android/ServerPingWithAlarmManager.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014-2021 Florian Schmaus + * Copyright © 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.os.Build; import android.os.SystemClock; /** @@ -121,6 +122,7 @@ public final class ServerPingWithAlarmManager extends Manager { private static final BroadcastReceiver ALARM_BROADCAST_RECEIVER = new BroadcastReceiver() { @Override + @SuppressWarnings("LockOnNonEnclosingClassLiteral") public void onReceive(Context context, Intent intent) { LOGGER.fine("Ping Alarm broadcast received"); Set> managers; @@ -163,7 +165,7 @@ public final class ServerPingWithAlarmManager extends Manager { private static AlarmManager sAlarmManager; /** - * Register a pending intent with the AlarmManager to be broadcasted every half hour and + * Register a pending intent with the AlarmManager to be broadcast every half hour and * register the alarm broadcast receiver to receive this intent. The receiver will check all * known questions if a ping is Necessary when invoked by the alarm intent. * @@ -173,7 +175,11 @@ public final class ServerPingWithAlarmManager extends Manager { sContext = context; context.registerReceiver(ALARM_BROADCAST_RECEIVER, new IntentFilter(PING_ALARM_ACTION)); sAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - sPendingIntent = PendingIntent.getBroadcast(context, 0, new Intent(PING_ALARM_ACTION), 0); + int pendingIntentFlags = 0; + if (Build.VERSION.SDK_INT >= 23) { + pendingIntentFlags |= PendingIntent.FLAG_IMMUTABLE; + } + sPendingIntent = PendingIntent.getBroadcast(context, 0, new Intent(PING_ALARM_ACTION), pendingIntentFlags); sAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_HALF_HOUR, AlarmManager.INTERVAL_HALF_HOUR, sPendingIntent); diff --git a/smack-android/build.gradle b/smack-android/build.gradle index 556a526eb..ef158e137 100644 --- a/smack-android/build.gradle +++ b/smack-android/build.gradle @@ -1,3 +1,9 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' + id 'org.igniterealtime.smack.android-boot-classpath-conventions' +} + description = """\ Smack for Android. All the required dependencies to run Smack on Android. @@ -16,13 +22,13 @@ dependencies { // used in non-Android projects. implementation "org.minidns:minidns-android21:$miniDnsVersion" - // androidProjects lists all projects that are checked to compile against android.jar - // Filter out the optional Smack dependencies from androidProjects - (androidProjects - androidOptionalProjects) - .each { project -> - api project - } + api project(':smack-core') + api project(':smack-im') + api project(':smack-resolver-minidns') + api project(':smack-sasl-provided') + api project(':smack-xmlparser') + api project(':smack-xmlparser-xpp3') // Add the Android jar to the Eclipse .classpath. - compileClasspath files(androidBootClasspath) + implementation files(androidBootClasspath) } diff --git a/smack-android/src/main/java/org/jivesoftware/smack/android/AndroidSmackInitializer.java b/smack-android/src/main/java/org/jivesoftware/smack/android/AndroidSmackInitializer.java index a34fb1776..b66b56462 100644 --- a/smack-android/src/main/java/org/jivesoftware/smack/android/AndroidSmackInitializer.java +++ b/smack-android/src/main/java/org/jivesoftware/smack/android/AndroidSmackInitializer.java @@ -37,6 +37,8 @@ import org.minidns.dnsserverlookup.android21.AndroidUsingLinkProperties; public class AndroidSmackInitializer implements SmackInitializer { @Override + // Android deprecated StrictHostnameVerifier in API level 22 + @SuppressWarnings("deprecation") public List initialize() { SmackConfiguration.setDefaultHostnameVerifier(new StrictHostnameVerifier()); Base64.setEncoder(AndroidBase64Encoder.INSTANCE); diff --git a/smack-android/src/main/java/org/jivesoftware/smackx/debugger/android/AndroidDebugger.java b/smack-android/src/main/java/org/jivesoftware/smackx/debugger/android/AndroidDebugger.java index 068dab202..930fb9195 100644 --- a/smack-android/src/main/java/org/jivesoftware/smackx/debugger/android/AndroidDebugger.java +++ b/smack-android/src/main/java/org/jivesoftware/smackx/debugger/android/AndroidDebugger.java @@ -28,7 +28,7 @@ import android.util.Log; * implementation, therefore {@link org.jivesoftware.smack.debugger.JulDebugger} is preferred. *

* It is possible to not only print the raw sent and received stanzas but also the interpreted - * packets by Smack. By default interpreted packets won't be printed. To enable this feature + * packets by Smack. By default,interpreted packets won't be printed. To enable this feature * just change the printInterpreted static variable to true. * */ diff --git a/smack-android/src/main/java/org/jivesoftware/smackx/package-info.java b/smack-android/src/main/java/org/jivesoftware/smackx/package-info.java index 7ebe86244..bce3b6064 120000 --- a/smack-android/src/main/java/org/jivesoftware/smackx/package-info.java +++ b/smack-android/src/main/java/org/jivesoftware/smackx/package-info.java @@ -1 +1 @@ -../../../../../../../smack-java8-full/src/main/java/org/jivesoftware/smackx/package-info.java \ No newline at end of file +../../../../../../../smack-java11-full/src/main/java/org/jivesoftware/smackx/package-info.java \ No newline at end of file diff --git a/smack-bosh/build.gradle b/smack-bosh/build.gradle index fcd3d5896..d939a9e46 100644 --- a/smack-bosh/build.gradle +++ b/smack-bosh/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} + description = """\ Smack BOSH API. This API is considered beta quality.""" diff --git a/smack-core/build.gradle b/smack-core/build.gradle index f449ec5b7..1ffb92eb7 100644 --- a/smack-core/build.gradle +++ b/smack-core/build.gradle @@ -1,7 +1,7 @@ -// Note that this is also declared in the main build.gradle for -// subprojects, but since evaluationDependsOnChildren is enabled we -// need to declare it here too to have bundle{bnd{...}} available -apply plugin: 'biz.aQute.bnd.builder' +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} description = """\ Smack core components.""" @@ -16,6 +16,9 @@ dependencies { api "org.jxmpp:jxmpp-jid:$jxmppVersion" api "org.minidns:minidns-core:$miniDnsVersion" + // TODO: Migrate Junit4 tests to Junit5. + testImplementation "org.junit.vintage:junit-vintage-engine:$junitVersion" + testFixturesImplementation project(':smack-xmlparser-stax') testFixturesImplementation project(':smack-xmlparser-xpp3') @@ -28,7 +31,7 @@ dependencies { testFixturesApi "org.jxmpp:jxmpp-jid:$jxmppVersion:tests" testFixturesApi "org.xmlunit:xmlunit-core:$xmlUnitVersion" - // Explictily add assertj-core which is a dependency of + // Explicitly add assertj-core which is a dependency of // xmlunit-assertj, but gradle fails to resolves it with: // Execution failed for task ':smack-core:compileTestJava'. // > Could not resolve all files for configuration ':smack-core:testCompileClasspath'. @@ -59,7 +62,7 @@ task createVersionResource(type: CreateFileTask) { outputFile = new File(projectDir, 'src/main/resources/org.jivesoftware.smack/version') } -compileJava.dependsOn(createVersionResource) +processResources.dependsOn(createVersionResource) jar { bundle { diff --git a/smack-core/src/integration-test/java/org/jivesoftware/smack/MessageTest.java b/smack-core/src/integration-test/java/org/jivesoftware/smack/MessageTest.java index 75b4f6915..bcded200c 100644 --- a/smack-core/src/integration-test/java/org/jivesoftware/smack/MessageTest.java +++ b/smack-core/src/integration-test/java/org/jivesoftware/smack/MessageTest.java @@ -60,7 +60,7 @@ public class MessageTest extends SmackTestCase { } Message message = (Message) collector.nextResult(2500); - assertNotNull("Message not recieved from remote user", message); + assertNotNull("Message not received from remote user", message); } /** diff --git a/smack-core/src/integration-test/java/org/jivesoftware/smack/test/SmackTestCase.java b/smack-core/src/integration-test/java/org/jivesoftware/smack/test/SmackTestCase.java index 601540abe..ae25506ba 100644 --- a/smack-core/src/integration-test/java/org/jivesoftware/smack/test/SmackTestCase.java +++ b/smack-core/src/integration-test/java/org/jivesoftware/smack/test/SmackTestCase.java @@ -487,7 +487,7 @@ public abstract class SmackTestCase extends TestCase { } /** - * Returns the name of the configuration file related to this test case. By default all + * Returns the name of the configuration file related to this test case. By default,all * the test cases will use the same configuration file. However, it's possible to override the * default configuration by providing a file of the form .xml * (e.g. RosterTest.xml). diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractConnectionListener.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractConnectionListener.java deleted file mode 100644 index f4a6749b9..000000000 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractConnectionListener.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * - * Copyright 2009 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smack; - -/** - * The AbstractConnectionListener class provides an empty implementation for all - * methods defined by the {@link ConnectionListener} interface. This is a - * convenience class which should be used in case you do not need to implement - * all methods. - * - * @author Henning Staib - * @deprecated use {@link ConnectionListener} instead. - */ -// TODO: Remove in Smack 4.5. -@Deprecated -public class AbstractConnectionListener implements ConnectionListener { - @Override - public void connected(XMPPConnection connection) { - // do nothing - } - - @Override - public void authenticated(XMPPConnection connection, boolean resumed) { - // do nothing - } - - @Override - public void connectionClosed() { - // do nothing - } - - @Override - public void connectionClosedOnError(Exception e) { - // do nothing - } - -} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index 2b2dd5ee8..a3fc2f941 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -1,6 +1,6 @@ /** * - * Copyright 2009 Jive Software, 2018-2022 Florian Schmaus. + * Copyright 2009 Jive Software, 2018-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package org.jivesoftware.smack; import java.io.IOException; import java.io.Reader; import java.io.Writer; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -128,7 +129,7 @@ import org.jxmpp.stringprep.XmppStringprepException; import org.jxmpp.util.XmppStringUtils; /** - * This abstract class is commonly used as super class for XMPP connection mechanisms like TCP and BOSH. Hence it + * This abstract class is commonly used as super class for XMPP connection mechanisms like TCP and BOSH. Hence, it * provides the methods for connection state management, like {@link #connect()}, {@link #login()} and * {@link #disconnect()} (which are deliberately not provided by the {@link XMPPConnection} interface). *

@@ -387,6 +388,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { * * @param configuration The configuration which is used to establish the connection. */ + @SuppressWarnings("this-escape") protected AbstractXMPPConnection(ConnectionConfiguration configuration) { saslAuthentication = new SASLAuthentication(this, configuration); config = configuration; @@ -1067,6 +1069,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { } @Override + @SuppressWarnings("TypeParameterUnusedInFormals") public I sendIqRequestAndWaitForResponse(IQ request) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { StanzaCollector collector = createStanzaCollectorAndSend(request); @@ -1214,7 +1217,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { } Stanza packet = (Stanza) sendTopLevelStreamElement; - final List listenersToNotify = new LinkedList<>(); + final List listenersToNotify = new ArrayList<>(); synchronized (sendListeners) { for (ListenerWrapper listenerWrapper : sendListeners.values()) { if (listenerWrapper.filterMatches(packet)) { @@ -1242,27 +1245,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { }); } - @Deprecated - @Override - public void addStanzaInterceptor(StanzaListener packetInterceptor, - StanzaFilter packetFilter) { - if (packetInterceptor == null) { - throw new NullPointerException("Packet interceptor is null."); - } - InterceptorWrapper interceptorWrapper = new InterceptorWrapper(packetInterceptor, packetFilter); - synchronized (interceptors) { - interceptors.put(packetInterceptor, interceptorWrapper); - } - } - - @Deprecated - @Override - public void removeStanzaInterceptor(StanzaListener packetInterceptor) { - synchronized (interceptors) { - interceptors.remove(packetInterceptor); - } - } - private static , MP extends MessageOrPresence> void addInterceptor( Map, GenericInterceptorWrapper> interceptors, Consumer interceptor, Predicate filter) { @@ -1305,7 +1287,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { private static , MP extends MessageOrPresence> MP fireMessageOrPresenceInterceptors( MP messageOrPresence, Map, GenericInterceptorWrapper> interceptors) { - List> interceptorsToInvoke = new LinkedList<>(); + List> interceptorsToInvoke = new ArrayList<>(); synchronized (interceptors) { for (GenericInterceptorWrapper interceptorWrapper : interceptors.values()) { if (interceptorWrapper.filterMatches(messageOrPresence)) { @@ -1340,7 +1322,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { * @return the, potentially modified stanza, after the interceptors are run. */ private Stanza firePacketInterceptors(Stanza packet) { - List interceptorsToInvoke = new LinkedList<>(); + List interceptorsToInvoke = new ArrayList<>(); synchronized (interceptors) { for (InterceptorWrapper interceptorWrapper : interceptors.values()) { if (interceptorWrapper.filterMatches(packet)) { @@ -1625,7 +1607,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { // First handle the async recv listeners. Note that this code is very similar to what follows a few lines below, // the only difference is that asyncRecvListeners is used here and that the packet listeners are started in // their own thread. - final Collection listenersToNotify = new LinkedList<>(); + final Collection listenersToNotify = new ArrayList<>(); extractMatchingListeners(packet, asyncRecvListeners, listenersToNotify); for (final StanzaListener listener : listenersToNotify) { asyncGoLimited(new Runnable() { @@ -1951,7 +1933,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { // Default implementation does nothing } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) @Override public F getFeature(QName qname) { return (F) streamFeatures.get(qname); @@ -2196,6 +2178,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { * {@link #maxAsyncRunnables}. Note that we use a {@code LinkedList} in order to avoid space blowups in case the * list ever becomes very big and shrinks again. */ + @SuppressWarnings("JdkObsolete") private final Queue deferredAsyncRunnables = new LinkedList<>(); private int deferredAsyncRunnablesCount; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AsyncButOrdered.java b/smack-core/src/main/java/org/jivesoftware/smack/AsyncButOrdered.java index 526e3cd2e..5788370ff 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AsyncButOrdered.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AsyncButOrdered.java @@ -53,14 +53,14 @@ import java.util.concurrent.Executor; public class AsyncButOrdered { /** - * A map with the currently pending runnables for a given key. Note that this is a weak hash map so we do not have - * to take care of removing the keys ourselfs from the map. + * A map with the currently pending runnables for a given key. Note that this is a weak hash map, so we do not have + * to take care of removing the keys ourselves from the map. */ private final Map> pendingRunnables = new WeakHashMap<>(); /** * A marker map if there is an active thread for the given key. Holds the responsible handler thread if one is - * active, otherwise the key is non-existend in the map. + * active, otherwise the key is non-existent in the map. */ private final Map threadActiveMap = new HashMap<>(); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java index 9561a158d..e9d8afe45 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2017-2022 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2017-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -88,7 +88,7 @@ import org.minidns.util.InetAddressUtil; /** * The connection configuration used for XMPP client-to-server connections. A well configured XMPP service will * typically only require you to provide two parameters: The XMPP address, also known as the JID, of the user and the - * password. All other configuration parameters could ideally be determined automatically by Smack. Hence it is often + * password. All other configuration parameters could ideally be determined automatically by Smack. Hence, it is often * enough to call {@link Builder#setXmppAddressAndPassword(CharSequence, String)}. *

* Technically there are typically at least two parameters required: Some kind of credentials for authentication. And @@ -248,7 +248,7 @@ public abstract class ConnectionConfiguration { stanzaIdSourceFactory = builder.stanzaIdSourceFactory; - // If the enabledSaslmechanisms are set, then they must not be empty + // If the enabledSaslMechanisms are set, then they must not be empty assert enabledSaslMechanisms == null || !enabledSaslMechanisms.isEmpty(); } @@ -267,7 +267,7 @@ public abstract class ConnectionConfiguration { context = SSLContext.getInstance("TLS"); } - // TODO: Remove the block below once we removed setKeystorePath(), setKeystoreType(), setCallbackHanlder() and + // TODO: Remove the block below once we removed setKeystorePath(), setKeystoreType(), setCallbackHandler() and // setPKCS11Library() in the builder, and all related fields and the parameters of this function. if (keyManagers == null) { keyManagers = Builder.getKeyManagersFrom(keystoreType, keystorePath, callbackHandler, pkcs11Library); @@ -534,7 +534,7 @@ public abstract class ConnectionConfiguration { /** * Returns the stream language to use when connecting to the server. * - * @return the stream language to use when connecting to the server. + * @return the stream language to use when connecting to the server or null. */ public Locale getLanguage() { return language; @@ -544,19 +544,21 @@ public abstract class ConnectionConfiguration { * Returns the xml:lang string of the stream language to use when connecting to the server. * *

If the developer sets the language to null, this will also return null, leading to - * the removal of the xml:lang tag from the stream. If a Locale("") is configured, this will - * return "", which can be used as an override.

+ * the removal of the xml:lang tag from the stream.

* - * @return the stream language to use when connecting to the server. + * @return the stream language to use when connecting to the server or null. */ public String getXmlLang() { - // TODO: Change to Locale.toLanguageTag() once Smack's minimum Android API level is 21 or higher. - // This will need a workaround for new Locale("").getLanguageTag() returning "und". Expected - // behavior of this function: - // - returns null if language is null - // - returns "" if language.getLanguage() returns the empty string - // - returns language.toLanguageTag() otherwise - return language != null ? language.toString().replace("_", "-") : null; + if (language == null) { + return null; + } + + String languageTag = language.toLanguageTag(); + if (languageTag.equals("und")) { + return null; + } + + return languageTag; } /** @@ -583,7 +585,7 @@ public abstract class ConnectionConfiguration { * Returns true if the connection is going to use stream compression. Stream compression * will be requested after TLS was established (if TLS was enabled) and only if the server * offered stream compression. With stream compression network traffic can be reduced - * up to 90%. By default compression is disabled. + * up to 90%. By default,compression is disabled. * * @return true if the connection is going to use stream compression. */ @@ -592,7 +594,7 @@ public abstract class ConnectionConfiguration { } /** - * Check if the given SASL mechansism is enabled in this connection configuration. + * Check if the given SASL mechanism is enabled in this connection configuration. * * @param saslMechanism TODO javadoc me please * @return true if the given SASL mechanism is enabled, false otherwise. @@ -607,7 +609,7 @@ public abstract class ConnectionConfiguration { /** * Return the explicitly enabled SASL mechanisms. May return null if no SASL mechanisms where - * explicitly enabled, i.e. all SALS mechanisms supported and announced by the service will be considered. + * explicitly enabled, i.e. all SASL mechanisms supported and announced by the service will be considered. * * @return the enabled SASL mechanisms or null. */ @@ -670,6 +672,7 @@ public abstract class ConnectionConfiguration { private boolean compressionEnabled = false; private StanzaIdSourceFactory stanzaIdSourceFactory = new StandardStanzaIdSource.Factory(); + @SuppressWarnings("this-escape") protected Builder() { if (SmackConfiguration.DEBUG) { enableDefaultDebugger(); @@ -857,22 +860,6 @@ public abstract class ConnectionConfiguration { return getThis(); } - /** - * Set the host to connect to by either its fully qualified domain name (FQDN) or its IP. - * - * @param fqdnOrIp a CharSequence either representing the FQDN or the IP of the host. - * @return a reference to this builder. - * @see #setHost(DnsName) - * @see #setHostAddress(InetAddress) - * @since 4.3.2 - * @deprecated use {@link #setHost(CharSequence)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public B setHostAddressByNameOrIp(CharSequence fqdnOrIp) { - return setHost(fqdnOrIp); - } - public B setPort(int port) { if (port < 0 || port > 65535) { throw new IllegalArgumentException( @@ -1021,25 +1008,6 @@ public abstract class ConnectionConfiguration { return getThis(); } - /** - * Sets a custom SSLContext for creating SSL sockets. - *

- * For more information on how to create a SSLContext see Java Secure Socket Extension (JSEE) Reference Guide: Creating Your Own X509TrustManager - * - * @param context the custom SSLContext for new sockets. - * @return a reference to this builder. - * @deprecated use {@link #setSslContextFactory(SslContextFactory)} instead}. - */ - // TODO: Remove in Smack 4.5. - @Deprecated - public B setCustomSSLContext(SSLContext context) { - return setSslContextFactory(() -> { - return context; - }); - } - /** * Sets a custom SSLContext for creating SSL sockets. *

@@ -1090,8 +1058,7 @@ public abstract class ConnectionConfiguration { } /** - * Sets if an initial available presence will be sent to the server. By default - * an available presence will be sent to the server indicating that this presence + * Sets if an initial available presence will be sent to the server. By default, * an available presence will be sent to the server indicating that this presence * is not online and available to receive messages. If you want to log in without * being 'noticed' then pass a false value. * @@ -1187,7 +1154,9 @@ public abstract class ConnectionConfiguration { if (!SASLAuthentication.isSaslMechanismRegistered(SASLMechanism.EXTERNAL)) { throw new IllegalArgumentException("SASL " + SASLMechanism.EXTERNAL + " is not registered"); } - setCustomSSLContext(sslContext); + setSslContextFactory(() -> { + return sslContext; + }); throwIfEnabledSaslMechanismsSet(); allowEmptyOrNullUsernames(); @@ -1266,7 +1235,7 @@ public abstract class ConnectionConfiguration { * Sets if the connection is going to use compression (default false). * * Compression is only activated if the server offers compression. With compression network - * traffic can be reduced up to 90%. By default compression is disabled. + * traffic can be reduced up to 90%. By default,compression is disabled. * * @param compressionEnabled if the connection is going to use compression on the HTTP level. * @return a reference to this object. @@ -1324,7 +1293,7 @@ public abstract class ConnectionConfiguration { } else { InputStream stream = TLSUtils.getDefaultTruststoreStreamIfPossible(); try { - // Note that PKCS12 keystores need a password one some Java platforms. Hence we try the famous + // Note that PKCS12 keystores need a password one some Java platforms. Hence, we try the famous // 'changeit' here. See https://bugs.openjdk.java.net/browse/JDK-8194702 char[] password = "changeit".toCharArray(); try { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java b/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java index 4685bf4af..7d545bb45 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java @@ -53,7 +53,7 @@ import org.jxmpp.jid.EntityBareJid; * *

Once TLS has been negotiated (i.e. the connection has been secured) it is possible to * register with the server or authenticate using SASL. If the - * server supports SASL then Smack will try to authenticate using SASL..

+ * server supports SASL then Smack will try to authenticate using SASL.

* *

The server may support many SASL mechanisms to use for authenticating. Out of the box * Smack provides several SASL mechanisms, but it is possible to register new SASL Mechanisms. Use diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ScheduledAction.java b/smack-core/src/main/java/org/jivesoftware/smack/ScheduledAction.java index da0377fab..def612a19 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ScheduledAction.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ScheduledAction.java @@ -1,6 +1,6 @@ /** * - * Copyright 2018 Florian Schmaus + * Copyright 2018-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,11 +50,13 @@ public class ScheduledAction implements Delayed { return smackReactor.cancel(this); } + @SuppressWarnings("JavaUtilDate") public boolean isDue() { Date now = new Date(); return now.after(releaseTime); } + @SuppressWarnings("JavaUtilDate") public long getTimeToDueMillis() { long now = System.currentTimeMillis(); return releaseTime.getTime() - now; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/Smack.java b/smack-core/src/main/java/org/jivesoftware/smack/Smack.java index 6608bba4b..daa7a18b4 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/Smack.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/Smack.java @@ -30,7 +30,7 @@ public class Smack { public static final String SMACK_PACKAGE = SMACK_ORG + ".smack"; /** - * Returns the Smack version information, eg "1.3.0". + * Returns the Smack version information, e.g."1.3.0". * * @return the Smack version information. */ diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java index f3316c8ed..ef1101cff 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java @@ -102,7 +102,7 @@ public final class SmackConfiguration { private static HostnameVerifier defaultHostnameVerififer; /** - * Returns the Smack version information, eg "1.3.0". + * Returns the Smack version information, e.g."1.3.0". * * @return the Smack version information. * @deprecated use {@link Smack#getVersion()} instead. diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java index 2a5b79c6a..82098094f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2014-2020 Florian Schmaus + * Copyright 2003-2007 Jive Software, 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,7 +63,7 @@ public final class SmackInitialization { private static final Logger LOGGER = Logger.getLogger(SmackInitialization.class.getName()); - /** + /* * Loads the configuration from the smack-config.xml and system properties file. *

* So far this means that: diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java index efc6ca602..8fc59ab3a 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java @@ -1,6 +1,6 @@ /** * - * Copyright 2018-2023 Florian Schmaus + * Copyright 2018-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -144,6 +144,7 @@ public class SmackReactor { } } + @SuppressWarnings("JavaUtilDate") ScheduledAction schedule(Runnable runnable, long delay, TimeUnit unit, ScheduledAction.Kind scheduledActionKind) { long releaseTimeEpoch = System.currentTimeMillis() + unit.toMillis(delay); Date releaseTimeDate = new Date(releaseTimeEpoch); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/StanzaCollector.java b/smack-core/src/main/java/org/jivesoftware/smack/StanzaCollector.java index 2ceaaf19d..dfa0ec7d2 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/StanzaCollector.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/StanzaCollector.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2016-2019 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2016-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -117,7 +117,7 @@ public final class StanzaCollector implements AutoCloseable { * @return the next stanza result, or null if there are no more * results. */ - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) public synchronized

P pollResult() { return (P) resultQueue.poll(); } @@ -134,6 +134,7 @@ public final class StanzaCollector implements AutoCloseable { * @return the next available packet. * @throws XMPPErrorException in case an error response. */ + @SuppressWarnings("TypeParameterUnusedInFormals") public

P pollResultOrThrow() throws XMPPErrorException { P result = pollResult(); if (result != null) { @@ -150,7 +151,7 @@ public final class StanzaCollector implements AutoCloseable { * @return the next available packet. * @throws InterruptedException if the calling thread was interrupted. */ - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) // TODO: Consider removing this method as it is hardly ever useful. public synchronized

P nextResultBlockForever() throws InterruptedException { throwIfCancelled(); @@ -175,6 +176,7 @@ public final class StanzaCollector implements AutoCloseable { * @return the next available packet. * @throws InterruptedException if the calling thread was interrupted. */ + @SuppressWarnings("TypeParameterUnusedInFormals") public

P nextResult() throws InterruptedException { return nextResult(connection.getReplyTimeout()); } @@ -191,7 +193,7 @@ public final class StanzaCollector implements AutoCloseable { * @return the next available stanza or null on timeout or connection error. * @throws InterruptedException if the calling thread was interrupted. */ - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) public

P nextResult(long timeout) throws InterruptedException { throwIfCancelled(); P res = null; @@ -223,6 +225,7 @@ public final class StanzaCollector implements AutoCloseable { * @throws NotConnectedException if the XMPP connection is not connected. * @see #nextResultOrThrow(long) */ + @SuppressWarnings("TypeParameterUnusedInFormals") public

P nextResultOrThrow() throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException { return nextResultOrThrow(connection.getReplyTimeout()); @@ -263,6 +266,7 @@ public final class StanzaCollector implements AutoCloseable { * @throws InterruptedException if the calling thread was interrupted. * @throws NotConnectedException if there was no response and the connection got disconnected. */ + @SuppressWarnings("TypeParameterUnusedInFormals") public

P nextResultOrThrow(long timeout) throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException { P result; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/StanzaListener.java b/smack-core/src/main/java/org/jivesoftware/smack/StanzaListener.java index 52b3168c8..786c5a8b0 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/StanzaListener.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/StanzaListener.java @@ -27,12 +27,6 @@ import org.jivesoftware.smack.packet.Stanza; * the {@link #processStanza(Stanza)} method will be called. This is the * opposite approach to the functionality provided by a {@link StanzaCollector} * which lets you block while waiting for results. - *

- * Additionally you are able to intercept Packets that are going to be send and - * make modifications to them. You can register a PacketListener as interceptor - * by using {@link XMPPConnection#addStanzaInterceptor(StanzaListener, - * org.jivesoftware.smack.filter.StanzaFilter)} - *

* * @see XMPPConnection#addAsyncStanzaListener(StanzaListener, org.jivesoftware.smack.filter.StanzaFilter) * @author Matt Tucker diff --git a/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java index f7543e724..45cd8328f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java @@ -96,7 +96,7 @@ import org.jxmpp.jid.EntityFullJid; *
  • other - e.g., {@link #addStanzaListener(StanzaListener, StanzaFilter)}
  • * *

    - * Asynchronous callbacks are run decoupled from the connections main event loop. Hence a callback triggered by + * Asynchronous callbacks are run decoupled from the connections main event loop. Hence, a callback triggered by * stanza B may (appear to) invoked before a callback triggered by stanza A, even though stanza A arrived before B. *

    *

    @@ -242,7 +242,7 @@ public interface XMPPConnection { *

    * * @param stanza the stanza to send. - * @return {@code true} if the stanza was successfully scheduled to be send, {@code false} otherwise. + * @return {@code true} if the stanza was successfully scheduled to be sent, {@code false} otherwise. * @throws NotConnectedException if the connection is not connected. * @since 4.4.0 * @deprecated use {@link #sendStanzaNonBlocking(Stanza)} instead. @@ -265,7 +265,7 @@ public interface XMPPConnection { * @param stanza the stanza to send. * @param timeout how long to wait before giving up, in units of {@code unit}. * @param unit a {@code TimeUnit} determining how to interpret the {@code timeout} parameter. - * @return {@code true} if the stanza was successfully scheduled to be send, {@code false} otherwise. + * @return {@code true} if the stanza was successfully scheduled to be sent, {@code false} otherwise. * @throws NotConnectedException if the connection is not connected. * @throws InterruptedException if the calling thread was interrupted. * @since 4.4.0 @@ -318,6 +318,7 @@ public interface XMPPConnection { * @throws InterruptedException if the calling thread was interrupted. * @since 4.3 */ + @SuppressWarnings("TypeParameterUnusedInFormals") I sendIqRequestAndWaitForResponse(IQ request) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException; @@ -423,7 +424,6 @@ public interface XMPPConnection { * * @param stanzaListener the stanza listener to notify of new received stanzas. * @param stanzaFilter the stanza filter to use. - * @see #addStanzaInterceptor(StanzaListener, StanzaFilter) * @since 4.1 */ void addSyncStanzaListener(StanzaListener stanzaListener, StanzaFilter stanzaFilter); @@ -449,7 +449,6 @@ public interface XMPPConnection { * * @param stanzaListener the stanza listener to notify of new received stanzas. * @param stanzaFilter the stanza filter to use. - * @see #addStanzaInterceptor(StanzaListener, StanzaFilter) * @since 4.1 */ void addAsyncStanzaListener(StanzaListener stanzaListener, StanzaFilter stanzaFilter); @@ -483,34 +482,6 @@ public interface XMPPConnection { */ void removeStanzaSendingListener(StanzaListener stanzaListener); - /** - * Registers a stanza interceptor with this connection. The interceptor will be - * invoked every time a stanza is about to be sent by this connection. Interceptors - * may modify the stanza to be sent. A stanza filter determines which stanzas - * will be delivered to the interceptor. - * - *

    - * NOTE: For a similar functionality on incoming stanzas, see {@link #addAsyncStanzaListener(StanzaListener, StanzaFilter)}. - *

    - * - * @param stanzaInterceptor the stanza interceptor to notify of stanzas about to be sent. - * @param stanzaFilter the stanza filter to use. - * @deprecated use {@link #addMessageInterceptor(Consumer, Predicate)} or {@link #addPresenceInterceptor(Consumer, Predicate)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - void addStanzaInterceptor(StanzaListener stanzaInterceptor, StanzaFilter stanzaFilter); - - /** - * Removes a stanza interceptor. - * - * @param stanzaInterceptor the stanza interceptor to remove. - * @deprecated use {@link #removeMessageInterceptor(Consumer)} or {@link #removePresenceInterceptor(Consumer)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - void removeStanzaInterceptor(StanzaListener stanzaInterceptor); - /** * Registers a stanza interceptor with this connection. The interceptor will be * invoked every time a stanza is about to be sent by this connection. Interceptors @@ -611,23 +582,6 @@ public interface XMPPConnection { */ FromMode getFromMode(); - /** - * Get the feature stanza extensions for a given stream feature of the - * server, or null if the server doesn't support that feature. - * - * @param {@link ExtensionElement} type of the feature. - * @param element TODO javadoc me please - * @param namespace TODO javadoc me please - * @return a stanza extensions of the feature or null - * @deprecated use {@link #getFeature(Class)} instead. - */ - // TODO: Remove in Smack 4.5. - @Deprecated - default F getFeature(String element, String namespace) { - QName qname = new QName(namespace, element); - return getFeature(qname); - } - /** * Get the feature stanza extensions for a given stream feature of the * server, or null if the server doesn't support that feature. @@ -637,6 +591,7 @@ public interface XMPPConnection { * @return a stanza extensions of the feature or null * @since 4.4 */ + @SuppressWarnings("TypeParameterUnusedInFormals") F getFeature(QName qname); /** diff --git a/smack-core/src/main/java/org/jivesoftware/smack/XmppInputOutputFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/XmppInputOutputFilter.java index 97fdff246..4f0c68b0a 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/XmppInputOutputFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/XmppInputOutputFilter.java @@ -57,8 +57,8 @@ public interface XmppInputOutputFilter { } /** - * The returned {@link ByteBuffer} is going to get fliped by the caller. The callee must not flip the buffer. - * @param inputData the data this methods needs to process. + * The returned {@link ByteBuffer} is going to get flipped by the caller. The callee must not flip the buffer. + * @param inputData the data this method needs to process. * @return a {@link ByteBuffer} or {@code null} if no data could be produced. * @throws IOException in case an I/O exception occurs. */ diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java index b24b00704..ca3de8190 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java @@ -301,7 +301,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne currentStateVertex = StateDescriptorGraph.convertToStateGraph(initialStateDescriptorVertex, connectionInternal); } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) public > CM getConnectionModuleFor( Class descriptorClass) { return (CM) connectionModules.get(descriptorClass); @@ -390,7 +390,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne // Ignore successorStateVertex if the only way to the final state is via the initial state. This happens // typically if we are in the ConnectedButUnauthenticated state on the way to ResourceboundAndAuthenticated, - // where we do not want to walk via InstantShutdown/Shtudown in a cycle over the initial state towards this + // where we do not want to walk via InstantShutdown/Shutdown in a cycle over the initial state towards this // state. if (walkStateGraphContext.wouldCauseCycle(successorStateVertex)) { // Ignore this successor. @@ -658,6 +658,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne updateOutgoingStreamXmlEnvironmentOnStreamOpen(streamOpen); } + @SuppressWarnings("this-escape") public static class DisconnectedStateDescriptor extends StateDescriptor { protected DisconnectedStateDescriptor() { super(DisconnectedState.class, StateDescriptor.Property.finalState); @@ -666,7 +667,8 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } private final class DisconnectedState extends State { - + // Invoked via reflection. + @SuppressWarnings("UnusedMethod") private DisconnectedState(StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) { super(stateDescriptor, connectionInternal); @@ -707,6 +709,8 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne private final class LookupRemoteConnectionEndpointsState extends State { boolean outgoingElementsQueueWasShutdown; + // Invoked via reflection. + @SuppressWarnings("UnusedMethod") private LookupRemoteConnectionEndpointsState(StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) { super(stateDescriptor, connectionInternal); @@ -817,6 +821,8 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } private final class ConnectedButUnauthenticatedState extends State { + // Invoked via reflection. + @SuppressWarnings("UnusedMethod") private ConnectedButUnauthenticatedState(StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) { super(stateDescriptor, connectionInternal); @@ -849,6 +855,8 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } private final class SaslAuthenticationState extends State { + // Invoked via reflection. + @SuppressWarnings("UnusedMethod") private SaslAuthenticationState(StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) { super(stateDescriptor, connectionInternal); @@ -892,6 +900,8 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } public static final class ResourceBindingStateDescriptor extends StateDescriptor { + // Invoked via reflection. + @SuppressWarnings("UnusedMethod") private ResourceBindingStateDescriptor() { super(ResourceBindingState.class, "RFC 6120 § 7"); addSuccessor(AuthenticatedAndResourceBoundStateDescriptor.class); @@ -899,6 +909,8 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } private final class ResourceBindingState extends State { + // Invoked via reflection. + @SuppressWarnings("UnusedMethod") private ResourceBindingState(StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) { super(stateDescriptor, connectionInternal); @@ -954,6 +966,8 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } private final class AuthenticatedAndResourceBoundState extends State { + // Invoked via reflection. + @SuppressWarnings("UnusedMethod") private AuthenticatedAndResourceBoundState(StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) { super(stateDescriptor, connectionInternal); @@ -994,6 +1008,8 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } private final class ShutdownState extends State { + // Invoked via reflection. + @SuppressWarnings("UnusedMethod") private ShutdownState(StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) { super(stateDescriptor, connectionInternal); @@ -1056,6 +1072,8 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } private static final class InstantShutdownState extends NoOpState { + // Invoked via reflection. + @SuppressWarnings("UnusedMethod") private InstantShutdownState(ModularXmppClientToServerConnection connection, StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) { super(connection, stateDescriptor, connectionInternal); @@ -1077,6 +1095,8 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } private final class CloseConnectionState extends State { + // Invoked via reflection. + @SuppressWarnings("UnusedMethod") private CloseConnectionState(StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) { super(stateDescriptor, connectionInternal); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/compression/CompressionModuleDescriptor.java b/smack-core/src/main/java/org/jivesoftware/smack/compression/CompressionModuleDescriptor.java index 3cf43ee79..4fa0e6045 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/compression/CompressionModuleDescriptor.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/compression/CompressionModuleDescriptor.java @@ -1,6 +1,6 @@ /** * - * Copyright 2018-2020 Florian Schmaus + * Copyright 2018-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,8 @@ public class CompressionModuleDescriptor extends ModularXmppClientToServerConnec public static final class Builder extends ModularXmppClientToServerConnectionModuleDescriptor.Builder { + // Invoked via reflection. + @SuppressWarnings("UnusedMethod") private Builder(ModularXmppClientToServerConnectionConfiguration.Builder connectionConfigurationBuilder) { super(connectionConfigurationBuilder); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/compression/zlib/ZlibXmppCompressionFactory.java b/smack-core/src/main/java/org/jivesoftware/smack/compression/zlib/ZlibXmppCompressionFactory.java index 096147931..d851b0c44 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/compression/zlib/ZlibXmppCompressionFactory.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/compression/zlib/ZlibXmppCompressionFactory.java @@ -143,7 +143,9 @@ public final class ZlibXmppCompressionFactory extends XmppCompressionFactory { int bytesWritten = compressor.deflate(buffer, initialOutputBufferPosition, length, flushMode); int newOutputBufferPosition = initialOutputBufferPosition + bytesWritten; - outputBuffer.position(newOutputBufferPosition); + // Workaround for Android API not matching Java >=9 API. + // See https://issuetracker.google.com/issues/369219141 + ((java.nio.Buffer) outputBuffer).position(newOutputBufferPosition); totalBytesWritten += bytesWritten; @@ -156,7 +158,9 @@ public final class ZlibXmppCompressionFactory extends XmppCompressionFactory { increasedBufferSize = MINIMUM_OUTPUT_BUFFER_INCREASE; } ByteBuffer newCurrentOutputBuffer = ByteBuffer.allocate(increasedBufferSize); - outputBuffer.flip(); + // Workaround for Android API not matching Java >=9 API. + // See https://issuetracker.google.com/issues/369219141 + ((java.nio.Buffer) outputBuffer).flip(); newCurrentOutputBuffer.put(outputBuffer); outputBuffer = newCurrentOutputBuffer; } @@ -202,7 +206,9 @@ public final class ZlibXmppCompressionFactory extends XmppCompressionFactory { throw new IOException(e); } - outputBuffer.position(inflateOutputBufferOffset + bytesInflated); + // Workaround for Android API not matching Java >=9 API. + // See https://issuetracker.google.com/issues/369219141 + ((java.nio.Buffer) outputBuffer).position(inflateOutputBufferOffset + bytesInflated); decompressorOutBytes += bytesInflated; @@ -212,7 +218,9 @@ public final class ZlibXmppCompressionFactory extends XmppCompressionFactory { int increasedBufferSize = outputBuffer.capacity() * 2; ByteBuffer increasedOutputBuffer = ByteBuffer.allocate(increasedBufferSize); - outputBuffer.flip(); + // Workaround for Android API not matching Java >=9 API. + // See https://issuetracker.google.com/issues/369219141 + ((java.nio.Buffer) outputBuffer).flip(); increasedOutputBuffer.put(outputBuffer); outputBuffer = increasedOutputBuffer; } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/datatypes/UInt16.java b/smack-core/src/main/java/org/jivesoftware/smack/datatypes/UInt16.java index 271de8cbc..8df4cd2f4 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/datatypes/UInt16.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/datatypes/UInt16.java @@ -19,7 +19,7 @@ package org.jivesoftware.smack.datatypes; import org.jivesoftware.smack.util.NumberUtil; /** - * A number representing an unsigned 16-bit integer. Can be used for values with the XML schema type "xs:unsingedShort". + * A number representing an unsigned 16-bit integer. Can be used for values with the XML schema type "xs:unsignedShort". */ public final class UInt16 extends Scalar implements Comparable { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/debugger/ConsoleDebugger.java b/smack-core/src/main/java/org/jivesoftware/smack/debugger/ConsoleDebugger.java index a8ef6c00a..95c339a5d 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/debugger/ConsoleDebugger.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/debugger/ConsoleDebugger.java @@ -28,7 +28,7 @@ import org.jivesoftware.smack.util.ExceptionUtil; * even block the thread since only one thread may print at a time. *

    * It is possible to not only print the raw sent and received stanzas but also the interpreted - * packets by Smack. By default interpreted packets won't be printed. To enable this feature + * packets by Smack. By default,interpreted packets won't be printed. To enable this feature * just change the printInterpreted static variable to true. *

    * @@ -41,6 +41,7 @@ public class ConsoleDebugger extends AbstractDebugger { super(connection); } + @SuppressWarnings("JavaUtilDate") @Override protected void log(String logMessage) { String formatedDate; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/debugger/JulDebugger.java b/smack-core/src/main/java/org/jivesoftware/smack/debugger/JulDebugger.java index db0772271..015bb813b 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/debugger/JulDebugger.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/debugger/JulDebugger.java @@ -27,7 +27,7 @@ import org.jivesoftware.smack.XMPPConnection; * even block the thread since only one thread may print at a time. *

    * It is possible to not only print the raw sent and received stanzas but also the interpreted - * packets by Smack. By default interpreted packets won't be printed. To enable this feature + * packets by Smack. By default,interpreted packets won't be printed. To enable this feature * just change the printInterpreted static variable to true. *

    * diff --git a/smack-core/src/main/java/org/jivesoftware/smack/debugger/SmackDebugger.java b/smack-core/src/main/java/org/jivesoftware/smack/debugger/SmackDebugger.java index 82ca10b62..4f936b72d 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/debugger/SmackDebugger.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/debugger/SmackDebugger.java @@ -57,7 +57,7 @@ public abstract class SmackDebugger { * * @param user the user@host/resource that has just logged in */ - // TODO: Should be replaced with a connection listener authenticed(). + // TODO: Should be replaced with a connection listener authenticated(). public abstract void userHasLogged(EntityFullJid user); /** diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/NotFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/NotFilter.java index 006b5f4f9..5589b2109 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/NotFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/NotFilter.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software. + * Copyright 2003-2007 Jive Software, 2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.jivesoftware.smack.filter; +import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.util.Objects; @@ -43,4 +44,9 @@ public class NotFilter implements StanzaFilter { public boolean accept(Stanza packet) { return !filter.accept(packet); } + + public static NotFilter of(Class extensionElementClass) { + ExtensionElementFilter extensionElementFilter = new ExtensionElementFilter<>(extensionElementClass); + return new NotFilter(extensionElementFilter); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/StanzaIdFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/StanzaIdFilter.java index c0d507e50..8e5a5cc2c 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/StanzaIdFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/StanzaIdFilter.java @@ -18,6 +18,7 @@ package org.jivesoftware.smack.filter; import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smack.packet.StanzaView; import org.jivesoftware.smack.util.StringUtils; /** @@ -34,7 +35,7 @@ public class StanzaIdFilter implements StanzaFilter { * * @param stanza the stanza which the ID is taken from. */ - public StanzaIdFilter(Stanza stanza) { + public StanzaIdFilter(StanzaView stanza) { this(stanza.getStanzaId()); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/package-info.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/package-info.java index 01f2e0461..c130b93ff 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/package-info.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/package-info.java @@ -23,7 +23,7 @@ *
  • {@link StanzaIdFilter}: filters for stanzas with a particular stanza ID
  • *
  • {@link ToMatchesFilter}: filters for stanzas that are sent to a particular address
  • *
  • {@link FromMatchesFilter}: filters for stanzas that are sent from a particular address
  • - *
  • {@link ExtensionElementFilter}: filters for stanzas that have a particular stanza exentsion element
  • + *
  • {@link ExtensionElementFilter}: filters for stanzas that have a particular stanza extension element
  • *
  • {@link AndFilter}: implements the logical AND operation over two or more filters
  • *
  • {@link OrFilter}: implements the logical OR operation over two or more filters
  • *
  • {@link NotFilter}: implements the logical NOT operation on a filter
  • diff --git a/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptor.java b/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptor.java index 8f399480e..29c298c76 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptor.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptor.java @@ -118,7 +118,7 @@ public abstract class StateDescriptor { if (stateClassConstructor != null) { stateClassConstructor.setAccessible(true); } else { - // TODO: Add validation check that if stateClassConstructor is 'null' the cosntructState() method is overriden. + // TODO: Add validation check that if stateClassConstructor is 'null' the constructState() method is overridden. } String className = getClass().getSimpleName(); @@ -155,7 +155,7 @@ public abstract class StateDescriptor { clazz = Class.forName(clazzName); } catch (ClassNotFoundException e) { // The state descriptor class is not in classpath, which probably means that the smack module is not loaded - // into the classpath. Hence we can silently ignore that. + // into the classpath. Hence, we can silently ignore that. LOGGER.log(Level.FINEST, "Ignoring unknown state descriptor '" + clazzName + "'", e); return; } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptorGraph.java b/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptorGraph.java index 41bb29f59..45d23bb36 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptorGraph.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptorGraph.java @@ -1,6 +1,6 @@ /** * - * Copyright 2018-2021 Florian Schmaus + * Copyright 2018-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -215,7 +215,7 @@ public class StateDescriptorGraph { inferredForwardEdges.put(predecessor, backwardsEdge); } } - // Ensure that the intial node has their successors inferred. + // Ensure that the initial node has their successors inferred. for (Class inferredSuccessorOfInitialStateDescriptor : inferredForwardEdges.getAll(initialStatedescriptorClass)) { initialNode.getElement().addSuccessor(inferredSuccessorOfInitialStateDescriptor); } @@ -368,7 +368,7 @@ public class StateDescriptorGraph { } } - public static void stateDescriptorGraphToDot(Collection> vertexes, + public static void stateDescriptorGraphToDot(Collection> vertexes, PrintWriter dotOut, boolean breakStateName) { dotOut.append("digraph {\n"); dfs(vertexes, diff --git a/smack-core/src/main/java/org/jivesoftware/smack/initializer/UrlInitializer.java b/smack-core/src/main/java/org/jivesoftware/smack/initializer/UrlInitializer.java index ec7b047e7..33ced4f39 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/initializer/UrlInitializer.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/initializer/UrlInitializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2014-2018 Florian Schmaus + * Copyright 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ package org.jivesoftware.smack.initializer; import java.io.InputStream; import java.net.URI; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -42,7 +42,7 @@ public abstract class UrlInitializer implements SmackInitializer { public List initialize() { InputStream is = null; final ClassLoader classLoader = this.getClass().getClassLoader(); - final List exceptions = new LinkedList(); + final List exceptions = new ArrayList(); final String providerUriString = getProvidersUri(); if (providerUriString != null) { try { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/isr/InstantStreamResumptionModuleDescriptor.java b/smack-core/src/main/java/org/jivesoftware/smack/isr/InstantStreamResumptionModuleDescriptor.java index d26703eec..3e8171076 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/isr/InstantStreamResumptionModuleDescriptor.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/isr/InstantStreamResumptionModuleDescriptor.java @@ -41,6 +41,8 @@ public class InstantStreamResumptionModuleDescriptor extends ModularXmppClientTo public static final class Builder extends ModularXmppClientToServerConnectionModuleDescriptor.Builder { + // Unfinished implementation. + @SuppressWarnings("UnusedMethod") private Builder(ModularXmppClientToServerConnectionConfiguration.Builder connectionConfigurationBuilder) { super(connectionConfigurationBuilder); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractError.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractError.java index 147b4f031..50067fc14 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractError.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractError.java @@ -1,6 +1,6 @@ /** * - * Copyright 2014-2021 Florian Schmaus + * Copyright 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -108,6 +108,7 @@ public class AbstractError { * @param type of the ExtensionElement. * @return the extension, or null if it doesn't exist. */ + @SuppressWarnings("TypeParameterUnusedInFormals") public PE getExtension(String elementName, String namespace) { return PacketUtil.extensionElementFrom(extensions, elementName, namespace); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractTextElement.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractTextElement.java index 00e82d704..2f475fa06 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractTextElement.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractTextElement.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2017-2019 Florian Schmaus + * Copyright © 2017-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,15 +54,4 @@ public abstract class AbstractTextElement implements ExtensionElement { return lang; } - /** - * Deprecated. - * - * @return deprecated - * @deprecated use {@link #getLanguage()} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public final String getLang() { - return lang; - } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/EmptyResultIQ.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/EmptyResultIQ.java index a4d977ac3..246e9de06 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/EmptyResultIQ.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/EmptyResultIQ.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014-2023 Florian Schmaus + * Copyright © 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ public class EmptyResultIQ extends IQ { } // TODO: Deprecate when stanza builder and parsing logic is ready. + @SuppressWarnings("this-escape") public EmptyResultIQ() { super((String) null, null); setType(IQ.Type.result); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/ExtensionElement.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/ExtensionElement.java index 5a7f00650..5f4ed1b6e 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/ExtensionElement.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/ExtensionElement.java @@ -19,7 +19,7 @@ package org.jivesoftware.smack.packet; /** * Interface to represent XMPP extension elements. Unlike {@link XmlElement}, every non-abstract class that implements * {@link ExtensionElement} must have a static final QNAME member of the type {@link javax.xml.namespace.QName}. This - * allows type-safe functions like {@link StanzaView#getExtension(Class)}. Hence this is a marker interface. + * allows type-safe functions like {@link StanzaView#getExtension(Class)}. Hence, this is a marker interface. *

    * Use this class when implementing new extension elements when possible. This means that every instance of your * implemented class must represent an XML element of the same qualified name. diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java index 1881f4c23..98f9880f3 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java @@ -88,14 +88,14 @@ public abstract class IQ extends Stanza implements IqView { } @Override - public Type getType() { + public final Type getType() { return type; } /** * Sets the type of the IQ packet. *

    - * Since the type of an IQ must present, an IllegalArgmentException will be thrown when type is + * Since the type of an IQ must present, an IllegalArgumentException will be thrown when type is * null. *

    * @@ -182,7 +182,7 @@ public abstract class IQ extends Stanza implements IqView { // Add the query section if there is one. IQChildElementXmlStringBuilder iqChildElement = getIQChildElementBuilder( new IQChildElementXmlStringBuilder(getChildElementName(), getChildElementNamespace(), null, xml.getXmlEnvironment())); - // TOOD: Document the cases where iqChildElement is null but childElementName not. And if there are none, change + // TODO: Document the cases where iqChildElement is null but childElementName not. And if there are none, change // the logic. if (iqChildElement == null) { return; @@ -287,20 +287,6 @@ public abstract class IQ extends Stanza implements IqView { return ErrorIQ.createErrorResponse(request, error); } - /** - * Deprecated. - * - * @param request the request. - * @param error the error. - * @return an error IQ. - * @deprecated use {@link #createErrorResponse(IQ, StanzaError)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public static ErrorIQ createErrorResponse(final IQ request, final StanzaError.Builder error) { - return createErrorResponse(request, error.build()); - } - public static ErrorIQ createErrorResponse(final IQ request, final StanzaError.Condition condition) { return createErrorResponse(request, StanzaError.getBuilder(condition).build()); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Mechanisms.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Mechanisms.java index a981589ee..2d36eb920 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Mechanisms.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Mechanisms.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014-2020 Florian Schmaus + * Copyright © 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,9 @@ */ package org.jivesoftware.smack.packet; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import javax.xml.namespace.QName; @@ -31,7 +31,7 @@ public class Mechanisms implements ExtensionElement { public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl"; public static final QName QNAME = new QName(NAMESPACE, ELEMENT); - public final List mechanisms = new LinkedList(); + public final List mechanisms = new ArrayList(); public Mechanisms(String mechanism) { mechanisms.add(mechanism); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java index d47cafda7..6063af13b 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java @@ -17,7 +17,6 @@ package org.jivesoftware.smack.packet; -import java.util.List; import java.util.Locale; import javax.xml.namespace.QName; @@ -29,10 +28,6 @@ import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.XmlStringBuilder; -import org.jxmpp.jid.Jid; -import org.jxmpp.jid.impl.JidCreate; -import org.jxmpp.stringprep.XmppStringprepException; - /** * Represents XMPP message packets. A message can be one of several types: * @@ -63,85 +58,7 @@ public final class Message extends MessageOrPresence public static final String ELEMENT = "message"; public static final String BODY = "body"; - private Type type; - - /** - * Creates a new, "normal" message. - * @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public Message() { - } - - /** - * Creates a new "normal" message to the specified recipient. - * - * @param to the recipient of the message. - * @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public Message(Jid to) { - setTo(to); - } - - /** - * Creates a new message of the specified type to a recipient. - * - * @param to the user to send the message to. - * @param type the message type. - * @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public Message(Jid to, Type type) { - this(to); - setType(type); - } - - /** - * Creates a new message to the specified recipient and with the specified body. - * - * @param to the user to send the message to. - * @param body the body of the message. - * @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public Message(Jid to, String body) { - this(to); - setBody(body); - } - - /** - * Creates a new message to the specified recipient and with the specified body. - * - * @param to the user to send the message to. - * @param body the body of the message. - * @throws XmppStringprepException if 'to' is not a valid XMPP address. - * @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public Message(String to, String body) throws XmppStringprepException { - this(JidCreate.from(to), body); - } - - /** - * Creates a new message with the specified recipient and extension element. - * - * @param to TODO javadoc me please - * @param extensionElement TODO javadoc me please - * @since 4.2 - * @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public Message(Jid to, ExtensionElement extensionElement) { - this(to); - addExtension(extensionElement); - } + private final Type type; Message(MessageBuilder messageBuilder) { super(messageBuilder); @@ -170,197 +87,6 @@ public final class Message extends MessageOrPresence return type; } - /** - * Sets the type of the message. - * - * @param type the type of the message. - * @deprecated use {@link StanzaBuilder} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public void setType(Type type) { - this.type = type; - } - - /** - * Sets the subject of the message. The subject is a short description of - * message contents. - * - * @param subject the subject of the message. - * @deprecated use {@link StanzaBuilder} instead. - */ - @Deprecated - // TODO: Remove when stanza builder is ready. - public void setSubject(String subject) { - if (subject == null) { - removeSubject(""); // use empty string because #removeSubject(null) is ambiguous - return; - } - addSubject(null, subject); - } - - /** - * Adds a subject with a corresponding language. - * - * @param language the language of the subject being added. - * @param subject the subject being added to the message. - * @return the new {@link org.jivesoftware.smack.packet.Message.Subject} - * @throws NullPointerException if the subject is null, a null pointer exception is thrown - */ - @Deprecated - // TODO: Remove when stanza builder is ready. - public Subject addSubject(String language, String subject) { - language = Stanza.determineLanguage(this, language); - - List currentSubjects = getExtensions(Subject.class); - for (Subject currentSubject : currentSubjects) { - if (language.equals(currentSubject.getLanguage())) { - throw new IllegalArgumentException("Subject with the language " + language + " already exists"); - } - } - - Subject messageSubject = new Subject(language, subject); - addExtension(messageSubject); - return messageSubject; - } - - /** - * Removes the subject with the given language from the message. - * - * @param language the language of the subject which is to be removed - * @return true if a subject was removed and false if it was not. - */ - @Deprecated - // TODO: Remove when stanza builder is ready. - public boolean removeSubject(String language) { - language = Stanza.determineLanguage(this, language); - for (Subject subject : getExtensions(Subject.class)) { - if (language.equals(subject.language)) { - return removeSubject(subject); - } - } - return false; - } - - /** - * Removes the subject from the message and returns true if the subject was removed. - * - * @param subject the subject being removed from the message. - * @return true if the subject was successfully removed and false if it was not. - */ - @Deprecated - // TODO: Remove when stanza builder is ready. - public boolean removeSubject(Subject subject) { - return removeExtension(subject) != null; - } - - /** - * Sets the body of the message. - * - * @param body the body of the message. - * @see #setBody(String) - * @since 4.2 - * @deprecated use {@link StanzaBuilder} instead. - */ - @Deprecated - // TODO: Remove when stanza builder is ready. - public void setBody(CharSequence body) { - String bodyString; - if (body != null) { - bodyString = body.toString(); - } else { - bodyString = null; - } - setBody(bodyString); - } - - /** - * Sets the body of the message. The body is the main message contents. - * - * @param body the body of the message. - * @deprecated use {@link StanzaBuilder} instead. - */ - @Deprecated - // TODO: Remove when stanza builder is ready. - public void setBody(String body) { - if (body == null) { - removeBody(""); // use empty string because #removeBody(null) is ambiguous - return; - } - addBody(null, body); - } - - /** - * Adds a body with a corresponding language. - * - * @param language the language of the body being added. - * @param body the body being added to the message. - * @return the new {@link org.jivesoftware.smack.packet.Message.Body} - * @throws NullPointerException if the body is null, a null pointer exception is thrown - * @since 3.0.2 - * @deprecated use {@link StanzaBuilder} instead. - */ - @Deprecated - // TODO: Remove when stanza builder is ready. - public Body addBody(String language, String body) { - language = Stanza.determineLanguage(this, language); - - removeBody(language); - - Body messageBody = new Body(language, body); - addExtension(messageBody); - return messageBody; - } - - /** - * Removes the body with the given language from the message. - * - * @param language the language of the body which is to be removed - * @return true if a body was removed and false if it was not. - * @deprecated use {@link StanzaBuilder} instead. - */ - @Deprecated - // TODO: Remove when stanza builder is ready. - public boolean removeBody(String language) { - language = Stanza.determineLanguage(this, language); - for (Body body : getBodies()) { - String bodyLanguage = body.getLanguage(); - if (Objects.equals(bodyLanguage, language)) { - removeExtension(body); - return true; - } - } - return false; - } - - /** - * Removes the body from the message and returns true if the body was removed. - * - * @param body the body being removed from the message. - * @return true if the body was successfully removed and false if it was not. - * @since 3.0.2 - * @deprecated use {@link StanzaBuilder} instead. - */ - @Deprecated - // TODO: Remove when stanza builder is ready. - public boolean removeBody(Body body) { - XmlElement removedElement = removeExtension(body); - return removedElement != null; - } - - /** - * Sets the thread id of the message, which is a unique identifier for a sequence - * of "chat" messages. - * - * @param thread the thread id of the message. - * @deprecated use {@link StanzaBuilder} instead. - */ - @Deprecated - // TODO: Remove when stanza builder is ready. - public void setThread(String thread) { - addExtension(new Message.Thread(thread)); - } - @Override public String getElementName() { return ELEMENT; @@ -412,22 +138,6 @@ public final class Message extends MessageOrPresence return buf; } - /** - * Creates and returns a copy of this message stanza. - *

    - * This does not perform a deep clone, as extension elements are shared between the new and old - * instance. - *

    - * @return a clone of this message. - * @deprecated use {@link #asBuilder()} instead. - */ - // TODO: Remove in Smack 4.5. - @Deprecated - @Override - public Message clone() { - return new Message(this); - } - /** * Represents a message subject, its language and the content of the subject. */ diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageOrPresence.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageOrPresence.java index f48b794a0..c3aa3d180 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageOrPresence.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageOrPresence.java @@ -20,11 +20,6 @@ import org.jivesoftware.smack.XMPPConnection; public abstract class MessageOrPresence> extends Stanza { - @Deprecated - // TODO: Remove in Smack 4.5. - protected MessageOrPresence() { - } - protected MessageOrPresence(StanzaBuilder stanzaBuilder) { super(stanzaBuilder); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Nonza.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Nonza.java index a86a4e1db..ece665543 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Nonza.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Nonza.java @@ -18,7 +18,7 @@ package org.jivesoftware.smack.packet; /** - * A Nonza, i.e everything that is not a stanza as defined + * A Nonza, i.e. everything that is not a stanza as defined * RFC 6120 8. Stanzas are {@link Message}, {@link Presence} and {@link IQ}. * Everything else should sublcass this class instead of {@link Stanza}. *

    diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java index 3490e1a23..caa3942dc 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2020-2021 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2020-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,12 +21,9 @@ import java.util.List; import java.util.Locale; import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.XmlStringBuilder; -import org.jxmpp.jid.Jid; - /** * Represents XMPP presence stanzas. Every presence stanza has a type, which is one of * the following values: @@ -78,55 +75,6 @@ public final class Presence extends MessageOrPresence private Mode mode = null; - /** - * Creates a new presence update. Status, priority, and mode are left un-set. - * - * @param type the type. - * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public Presence(Type type) { - // Ensure that the stanza ID is set by calling super(). - super(); - setType(type); - } - - /** - * Creates a new presence with the given type and using the given XMPP address as recipient. - * - * @param to the recipient. - * @param type the type. - * @since 4.2 - * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public Presence(Jid to, Type type) { - this(type); - setTo(to); - } - - /** - * Creates a new presence update with a specified status, priority, and mode. - * - * @param type the type. - * @param status a text message describing the presence update. - * @param priority the priority of this presence update. - * @param mode the mode type for this presence update. - * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public Presence(Type type, String status, int priority, Mode mode) { - // Ensure that the stanza ID is set by calling super(). - super(); - setType(type); - setStatus(status); - setPriority(priority); - setMode(mode); - } - Presence(PresenceBuilder presenceBuilder) { super(presenceBuilder); type = presenceBuilder.type; @@ -186,36 +134,11 @@ public final class Presence extends MessageOrPresence return type; } - /** - * Sets the type of the presence packet. - * - * @param type the type of the presence packet. - * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public void setType(Type type) { - this.type = Objects.requireNonNull(type, "Type cannot be null"); - } - @Override public String getStatus() { return status; } - /** - * Sets the status message of the presence update. The status is free-form text - * describing a user's presence (i.e., "gone to lunch"). - * - * @param status the status message. - * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public void setStatus(String status) { - this.status = status; - } - @Override public int getPriority() { return getPriorityByte(); @@ -233,20 +156,11 @@ public final class Presence extends MessageOrPresence * Sets the priority of the presence. The valid range is -128 through 127. * * @param priority the priority of the presence. - * @throws IllegalArgumentException if the priority is outside the valid range. * @see RFC 6121 § 4.7.2.3. Priority Element * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. */ @Deprecated - // TODO: Remove in Smack 4.5. - public void setPriority(int priority) { - if (priority < -128 || priority > 127) { - throw new IllegalArgumentException("Priority value " + priority + - " is not valid. Valid range is -128 through 127."); - } - setPriority((byte) priority); - } - + // TODO: Remove in Smack 4.6. public void setPriority(byte priority) { this.priority = priority; } @@ -259,19 +173,6 @@ public final class Presence extends MessageOrPresence return mode; } - /** - * Sets the mode of the presence update. A null presence mode value is interpreted - * to be the same thing as {@link Presence.Mode#available}. - * - * @param mode the mode. - * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public void setMode(Mode mode) { - this.mode = mode; - } - @Override public String getElementName() { return ELEMENT; @@ -346,37 +247,6 @@ public final class Presence extends MessageOrPresence return buf; } - /** - * Creates and returns a copy of this presence stanza. - *

    - * This does not perform a deep clone, as extension elements are shared between the new and old - * instance. - *

    - * @return a clone of this presence. - * @deprecated use {@link #asBuilder()} instead. - */ - // TODO: Remove in Smack 4.5. - @Deprecated - @Override - public Presence clone() { - return new Presence(this); - } - - /** - * Clone this presence and set a newly generated stanza ID as the clone's ID. - * - * @return a "clone" of this presence with a different stanza ID. - * @since 4.1.2 - * @deprecated use {@link #asBuilder(XMPPConnection)} or {@link #asBuilder(String)}instead. - */ - // TODO: Remove in Smack 4.5. - @Deprecated - public Presence cloneWithNewId() { - Presence clone = clone(); - clone.setNewStanzaId(); - return clone; - } - /** * An enum to represent the presence type. Note that presence type is often confused * with presence mode. Generally, if a user is signed in to a server, they have a presence diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Session.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Session.java index 48e0330c6..7e6331bb8 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Session.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Session.java @@ -39,6 +39,7 @@ public class Session extends SimpleIQ { public static final String ELEMENT = "session"; public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-session"; + @SuppressWarnings("this-escape") public Session() { super(ELEMENT, NAMESPACE); setType(IQ.Type.set); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/StandardExtensionElement.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/StandardExtensionElement.java index 8ae1f5a7a..9f1959460 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/StandardExtensionElement.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/StandardExtensionElement.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2021 Florian Schmaus. + * Copyright 2015-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,9 +53,7 @@ public final class StandardExtensionElement implements XmlElement { /** * Constructs a new extension element with the given name and namespace and nothing else. - *

    * This is meant to construct extension elements used as simple flags in Stanzas. - *

    * * @param name the name of the extension element. * @param namespace the namespace of the extension element. diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java index 7123e82f8..88c30bbfa 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java @@ -98,7 +98,7 @@ public abstract class Stanza implements StanzaView, TopLevelStreamElement { protected Stanza(StanzaBuilder stanzaBuilder) { if (stanzaBuilder.stanzaIdSource != null) { id = stanzaBuilder.stanzaIdSource.getNewStanzaId(); - // Note that some stanza ID sources, e.g. StanzaBuilder.PresenceBuilder.EMPTY return null here. Hence we + // Note that some stanza ID sources, e.g. StanzaBuilder.PresenceBuilder.EMPTY return null here. Hence, we // only check that the returned string is not empty. assert StringUtils.isNullOrNotEmpty(id); usedStanzaIdSource = stanzaBuilder.stanzaIdSource; @@ -159,22 +159,6 @@ public abstract class Stanza implements StanzaView, TopLevelStreamElement { return id != null; } - /** - * Set the stanza id if none is set. - * - * @return the stanza id. - * @since 4.2 - * @deprecated use {@link StanzaBuilder} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public String setStanzaId() { - if (!hasStanzaIdSet()) { - setNewStanzaId(); - } - return getStanzaId(); - } - /** * Throws an {@link IllegalArgumentException} if this stanza has no stanza ID set. * @@ -219,7 +203,7 @@ public abstract class Stanza implements StanzaView, TopLevelStreamElement { * @param to who the packet is being sent to. */ // TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone. - public void setTo(Jid to) { + public final void setTo(Jid to) { this.to = to; } @@ -255,34 +239,11 @@ public abstract class Stanza implements StanzaView, TopLevelStreamElement { error = stanzaError; } - /** - * Deprecated. - * @param stanzaError the stanza error. - * @deprecated use {@link StanzaBuilder} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public void setError(StanzaError.Builder stanzaError) { - setError(stanzaError.build()); - } - @Override public final String getLanguage() { return language; } - /** - * Sets the xml:lang of this Stanza. - * - * @param language the xml:lang of this Stanza. - * @deprecated use {@link StanzaBuilder#setLanguage(String)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public void setLanguage(String language) { - this.language = language; - } - @Override public final List getExtensions() { synchronized (extensionElements) { @@ -374,22 +335,6 @@ public abstract class Stanza implements StanzaView, TopLevelStreamElement { return packetExtension; } - /** - * This method is deprecated. Use preferably {@link #getExtension(Class)} or {@link #getExtensionElement(String, String)}. - * - * @param the type to cast to. - * @param elementName the XML element name of the extension. (May be null) - * @param namespace the XML element namespace of the extension. - * @return the extension, or null if it doesn't exist. - * @deprecated use {@link #getExtension(Class)} or {@link #getExtensionElement(String, String)} instead. - */ - // TODO: Remove in Smack 4.5. - @SuppressWarnings("unchecked") - @Deprecated - public final E getExtension(String elementName, String namespace) { - return (E) getExtensionElement(elementName, namespace); - } - @Override public final XmlElement getExtension(QName qname) { synchronized (extensionElements) { @@ -501,27 +446,6 @@ public abstract class Stanza implements StanzaView, TopLevelStreamElement { } } - /** - * Removes a stanza extension from the packet. - * - * @param extension the stanza extension to remove. - * @return the removed stanza extension or null. - * @deprecated use {@link StanzaBuilder} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public final XmlElement removeExtension(XmlElement extension) { - QName key = extension.getQName(); - synchronized (extensionElements) { - List list = extensionElements.getAll(key); - boolean removed = list.remove(extension); - if (removed) { - return extension; - } - } - return null; - } - /** * Returns a short String describing the Stanza. This method is suited for log purposes. */ diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaBuilder.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaBuilder.java index ec8bdac3c..2cc2a20ed 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaBuilder.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaBuilder.java @@ -87,9 +87,9 @@ public abstract class StanzaBuilder> implements Stanz } /** - * Set the recipent address of the stanza. + * Set the recipient address of the stanza. * - * @param to whoe the stanza is being sent to. + * @param to whom the stanza is being sent. * @return a reference to this builder. * @throws XmppStringprepException if the provided character sequence is not a valid XMPP address. * @see #to(Jid) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaView.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaView.java index 85c4af1a9..e435d7159 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaView.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaView.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019-2021 Florian Schmaus + * Copyright 2019-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ public interface StanzaView extends XmlLangElement { /** * Returns who the stanza is being sent "to", or null if * the value is not set. The XMPP protocol often makes the "to" - * attribute optional, so it does not always need to be set.

    + * attribute optional, so it does not always need to be set. * * @return who the stanza is being sent to, or null if the * value has not been set. @@ -46,7 +46,7 @@ public interface StanzaView extends XmlLangElement { /** * Returns who the stanza is being sent "from" or null if * the value is not set. The XMPP protocol often makes the "from" - * attribute optional, so it does not always need to be set.

    + * attribute optional, so it does not always need to be set. * * @return who the stanza is being sent from, or null if the * value has not been set. diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/StreamError.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/StreamError.java index f034be9d3..a58599de8 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/StreamError.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/StreamError.java @@ -58,9 +58,9 @@ import org.jivesoftware.smack.util.XmlStringBuilder; * stream has been authenticated * policy-violation the entity has violated some local service * policy. - * remote-connection-failed Rthe server is unable to properly connect + * remote-connection-failed the server is unable to properly connect * to a remote entity. - * resource-constraint Rthe server lacks the system resources necessary + * resource-constraint the server lacks the system resources necessary * to service the stream. * restricted-xml the entity has attempted to send restricted XML * features. diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/UnparsedIQ.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/UnparsedIQ.java index e363d2e30..8cd2146f8 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/UnparsedIQ.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/UnparsedIQ.java @@ -16,6 +16,8 @@ */ package org.jivesoftware.smack.packet; +import org.jivesoftware.smack.util.StringUtils; + /** * An IQ stanzas that could not be parsed because no provider was found. */ @@ -34,7 +36,12 @@ public class UnparsedIQ extends IQ { @Override protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { - xml.escape(content); + if (StringUtils.isEmpty(content)) { + xml.setEmptyElement(); + } else { + xml.rightAngleBracket(); + xml.escape(content); + } return xml; } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/provider/ProviderFileLoader.java b/smack-core/src/main/java/org/jivesoftware/smack/provider/ProviderFileLoader.java index 11da95318..2781fe0d9 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/provider/ProviderFileLoader.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/provider/ProviderFileLoader.java @@ -17,9 +17,9 @@ package org.jivesoftware.smack.provider; import java.io.InputStream; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -39,11 +39,11 @@ import org.jivesoftware.smack.xml.XmlPullParser; public class ProviderFileLoader implements ProviderLoader { private static final Logger LOGGER = Logger.getLogger(ProviderFileLoader.class.getName()); - private final Collection iqProviders = new LinkedList(); - private final Collection extProviders = new LinkedList(); - private final Collection sfProviders = new LinkedList(); + private final Collection iqProviders = new ArrayList(); + private final Collection extProviders = new ArrayList(); + private final Collection sfProviders = new ArrayList(); - private List exceptions = new LinkedList(); + private List exceptions = new ArrayList(); public ProviderFileLoader(InputStream providerStream) { this(providerStream, ProviderFileLoader.class.getClassLoader()); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java b/smack-core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java index 0943f31d6..b2f08cc3a 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/provider/ProviderManager.java @@ -97,6 +97,7 @@ import org.jivesoftware.smack.util.XmppElementUtil; * </extensionProvider> * </smackProviders> * + *

    * If multiple provider entries attempt to register to handle the same element name and namespace, * the first entry loaded from the classpath will take precedence. Whenever a stanza extension * is found in a packet, parsing will be passed to the correct provider. Each provider @@ -106,7 +107,8 @@ import org.jivesoftware.smack.util.XmppElementUtil; * set the properties of th class using the values in the stanza extension sub-element. When an * extension provider is not registered for an element name and namespace combination, Smack will * store all top-level elements of the sub-packet in DefaultPacketExtension object and then - * attach it to the packet.

    + * attach it to the packet. + *

    * * @author Matt Tucker */ diff --git a/smack-core/src/main/java/org/jivesoftware/smack/provider/package-info.java b/smack-core/src/main/java/org/jivesoftware/smack/provider/package-info.java index 5e40c5639..21fafdfcb 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/provider/package-info.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/provider/package-info.java @@ -16,7 +16,7 @@ */ /** - * The Smack provider architecture is a system for plugging in custom XML parsing of staza extensions + * The Smack provider architecture is a system for plugging in custom XML parsing of stanza extensions * ({@link org.jivesoftware.smack.packet.ExtensionElement}, {@link org.jivesoftware.smack.packet.IQ} stanzas and * {@link org.jivesoftware.smack.packet.Nonza}. Hence, there are the the following providers: *
      diff --git a/smack-core/src/main/java/org/jivesoftware/smack/proxy/HTTPProxySocketConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/proxy/HTTPProxySocketConnection.java index 549406423..bdf0cf390 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/proxy/HTTPProxySocketConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/proxy/HTTPProxySocketConnection.java @@ -23,6 +23,7 @@ import java.io.StringReader; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.Socket; +import java.nio.charset.StandardCharsets; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -58,7 +59,7 @@ class HTTPProxySocketConnection implements ProxySocketConnection { proxyLine = "\r\nProxy-Authorization: Basic " + Base64.encode(username + ":" + password); } socket.getOutputStream().write((hostport + " HTTP/1.1\r\nHost: " - + host + ":" + port + proxyLine + "\r\n\r\n").getBytes("UTF-8")); + + host + ":" + port + proxyLine + "\r\n\r\n").getBytes(StandardCharsets.UTF_8)); InputStream in = socket.getInputStream(); StringBuilder got = new StringBuilder(100); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/proxy/ProxyInfo.java b/smack-core/src/main/java/org/jivesoftware/smack/proxy/ProxyInfo.java index 402a106a4..90433c5c8 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/proxy/ProxyInfo.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/proxy/ProxyInfo.java @@ -41,6 +41,7 @@ public class ProxyInfo { private ProxyType proxyType; private final ProxySocketConnection proxySocketConnection; + @SuppressWarnings("this-escape") public ProxyInfo(ProxyType pType, String pHost, int pPort, String pUser, String pPass) { this.proxyType = pType; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java index f1c00ef19..24330a6ac 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java @@ -358,7 +358,7 @@ public abstract class SASLMechanism implements Comparable { * SASLprep the given String. The resulting String is in UTF-8. * * @param string the String to sasl prep. - * @return the given String SASL preped + * @return the given String SASL prepped * @see RFC 4013 - SASLprep: Stringprep Profile for User Names and Passwords */ protected static String saslPrep(String string) { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/ScramMechanism.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/ScramMechanism.java index 8f4133c81..6dc482ce3 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/ScramMechanism.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/ScramMechanism.java @@ -271,6 +271,7 @@ public abstract class ScramMechanism extends SASLMechanism { return null; } + @SuppressWarnings("MixedMutabilityReturnType") private static Map parseAttributes(String string) throws SmackSaslException { if (string.length() == 0) { return Collections.emptyMap(); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java b/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java index 408e00bfd..c3566c6c0 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/CollectionUtil.java @@ -55,6 +55,7 @@ public class CollectionUtil { boolean test(T t); } + @SuppressWarnings("NonApiType") public static ArrayList newListWith(Collection collection) { if (collection == null) { return null; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/MAC.java b/smack-core/src/main/java/org/jivesoftware/smack/util/MAC.java index 4d78d24f8..df177204e 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/MAC.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/MAC.java @@ -33,7 +33,7 @@ public class MAC { HMAC_SHA1 = Mac.getInstance(HMACSHA1); } catch (NoSuchAlgorithmException e) { - // Smack wont be able to function normally if this exception is thrown, wrap it into + // Smack won't be able to function normally if this exception is thrown, wrap it into // an ISE and make the user aware of the problem. throw new IllegalStateException(e); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/MD5.java b/smack-core/src/main/java/org/jivesoftware/smack/util/MD5.java index 26c0547fd..d81c4de5a 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/MD5.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/MD5.java @@ -31,7 +31,7 @@ public class MD5 { MD5_DIGEST = MessageDigest.getInstance(StringUtils.MD5); } catch (NoSuchAlgorithmException e) { - // Smack wont be able to function normally if this exception is thrown, wrap it into + // Smack won't be able to function normally if this exception is thrown, wrap it into // an ISE and make the user aware of the problem. throw new IllegalStateException(e); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/MultiMap.java b/smack-core/src/main/java/org/jivesoftware/smack/util/MultiMap.java index 852e01bf7..7e9889e57 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/MultiMap.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/MultiMap.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2015-2021 Florian Schmaus + * Copyright © 2015-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -184,13 +184,14 @@ public class MultiMap { } /** - * Remove the given number of values for a given key. May return less values then requested. + * Remove the given number of values for a given key. May return less values than requested. * * @param key the key to remove from. * @param num the number of values to remove. * @return a list of the removed values. * @since 4.4.0 */ + @SuppressWarnings("MixedMutabilityReturnType") public List remove(K key, int num) { List values = map.get(key); if (values == null) { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/NumberUtil.java b/smack-core/src/main/java/org/jivesoftware/smack/util/NumberUtil.java index 98635309d..836796248 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/NumberUtil.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/NumberUtil.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2015-2020 Florian Schmaus + * Copyright © 2015-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,18 +18,6 @@ package org.jivesoftware.smack.util; public class NumberUtil { - /** - * Checks if the given long is within the range of an unsigned 32-bit integer, the XML type "xs:unsignedInt". - * - * @param value TODO javadoc me please - * @deprecated use {@link #requireUInt32(long)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public static void checkIfInUInt32Range(long value) { - requireUInt32(value); - } - /** * Checks if the given long is within the range of an unsigned 32-bit integer, the XML type "xs:unsignedInt". * diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java index 042402299..69f0aaca2 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2019-2023 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2019-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,6 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -87,7 +86,7 @@ public class PacketParserUtils { return parser; } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) public static S parseStanza(String stanza) throws XmlPullParserException, SmackParsingException, IOException { return (S) parseStanza(getParserFor(stanza), XmlEnvironment.EMPTY); } @@ -230,7 +229,7 @@ public class PacketParserUtils { // Assume this is the end tag of the start tag at the // beginning of this method. Typical examples where this // happens are body elements containing the empty string, - // ie. , which appears to be valid XMPP, or a + // i.e. , which appears to be valid XMPP, or a // least it's not explicitly forbidden by RFC 6121 5.2.3 return ""; } else { @@ -644,7 +643,7 @@ public class PacketParserUtils { assert parser.getEventType() == XmlPullParser.Event.START_ELEMENT; String name; final int initialDepth = parser.getDepth(); - List methods = new LinkedList<>(); + List methods = new ArrayList<>(); outerloop: while (true) { XmlPullParser.Event eventType = parser.next(); switch (eventType) { @@ -850,7 +849,7 @@ public class PacketParserUtils { throws XmlPullParserException, IOException { ParserUtils.assertAtStartTag(parser); assert parser.getNamespace().equals(StartTls.NAMESPACE); - int initalDepth = parser.getDepth(); + int initialDepth = parser.getDepth(); boolean required = false; outerloop: while (true) { XmlPullParser.Event event = parser.next(); @@ -864,7 +863,7 @@ public class PacketParserUtils { } break; case END_ELEMENT: - if (parser.getDepth() == initalDepth) { + if (parser.getDepth() == initialDepth) { break outerloop; } break; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/PacketUtil.java b/smack-core/src/main/java/org/jivesoftware/smack/util/PacketUtil.java index a68e91dd0..40da6f4d6 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/PacketUtil.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/PacketUtil.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014-2021 Florian Schmaus + * Copyright © 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,7 @@ public class PacketUtil { * * @return the extension element */ - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) public static PE extensionElementFrom(Collection collection, String element, String namespace) { for (XmlElement packetExtension : collection) { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/Pair.java b/smack-core/src/main/java/org/jivesoftware/smack/util/Pair.java index 08bf41234..13e65e881 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/Pair.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/Pair.java @@ -1,6 +1,6 @@ /** * - * Copyright 2020 Florian Schmaus. + * Copyright 2020-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,11 +26,12 @@ public final class Pair { this.second = second; } - public static Pair create(F first, S second) { + public static Pair create(F first, S second) { return new Pair<>(first, second); } - public static Pair createAndInitHashCode(F first, S second) { + @SuppressWarnings("ReturnValueIgnored") + public static Pair createAndInitHashCode(F first, S second) { Pair pair = new Pair<>(first, second); pair.hashCode(); return pair; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/ParserUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/ParserUtils.java index 64c18a7fb..7c58f8a12 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/ParserUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/ParserUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014-2023 Florian Schmaus + * Copyright © 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,6 @@ import java.text.ParseException; import java.util.Date; import java.util.Locale; -import javax.xml.namespace.QName; - import org.jivesoftware.smack.datatypes.UInt16; import org.jivesoftware.smack.datatypes.UInt32; import org.jivesoftware.smack.packet.XmlEnvironment; @@ -146,7 +144,7 @@ public class ParserUtils { } /** - * Prase a string to a boolean value as per "xs:boolean". Valid input strings are "true", "1" for true, and "false", "0" for false. + * Phrase a string to a boolean value as per "xs:boolean". Valid input strings are "true", "1" for true, and "false", "0" for false. * * @param booleanString the input string. * @return the boolean representation of the input string @@ -367,19 +365,6 @@ public class ParserUtils { return parser.getAttributeValue("http://www.w3.org/XML/1998/namespace", "lang"); } - /** - * Get the QName of the current element. - * - * @param parser the parser. - * @return the Qname. - * @deprecated use {@link XmlPullParser#getQName()} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5 - public static QName getQName(XmlPullParser parser) { - return parser.getQName(); - } - public static InternetAddress getInternetAddressIngoringZoneIdAttribute(XmlPullParser parser, String attribute) { String inetAddressString = parser.getAttributeValue(attribute); if (inetAddressString == null) { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/SHA1.java b/smack-core/src/main/java/org/jivesoftware/smack/util/SHA1.java index 89144530b..3c7d53ac4 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/SHA1.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/SHA1.java @@ -31,7 +31,7 @@ public class SHA1 { SHA1_DIGEST = MessageDigest.getInstance(StringUtils.SHA1); } catch (NoSuchAlgorithmException e) { - // Smack wont be able to function normally if this exception is thrown, wrap it into + // Smack won't be able to function normally if this exception is thrown, wrap it into // an ISE and make the user aware of the problem. throw new IllegalStateException(e); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/SecurityUtil.java b/smack-core/src/main/java/org/jivesoftware/smack/util/SecurityUtil.java index 3521279df..c796ab23e 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/SecurityUtil.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/SecurityUtil.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019 Florian Schmaus. + * Copyright 2019-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ public class SecurityUtil { private static final LruCache, Void> INSERTED_PROVIDERS_CACHE = new LruCache<>(8); + @SuppressWarnings("LockOnNonEnclosingClassLiteral") public static void ensureProviderAtFirstPosition(Class providerClass) { if (INSERTED_PROVIDERS_CACHE.containsKey(providerClass)) { return; @@ -41,7 +42,7 @@ public class SecurityUtil { String providerName = provider.getName(); - int installedPosition ; + int installedPosition; synchronized (Security.class) { Security.removeProvider(providerName); installedPosition = Security.insertProviderAt(provider, 1); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java index 5ebe4c116..83c9f40bb 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2016-2021 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2016-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,24 +36,6 @@ public class StringUtils { public static final String MD5 = "MD5"; public static final String SHA1 = "SHA-1"; - /** - * Deprecated, do not use. - * - * @deprecated use StandardCharsets.UTF_8 instead. - */ - // TODO: Remove in Smack 4.5. - @Deprecated - public static final String UTF8 = "UTF-8"; - - /** - * Deprecated, do not use. - * - * @deprecated use StandardCharsets.US_ASCII instead. - */ - // TODO: Remove in Smack 4.5. - @Deprecated - public static final String USASCII = "US-ASCII"; - public static final String QUOTE_ENCODE = """; public static final String APOS_ENCODE = "'"; public static final String AMP_ENCODE = "&"; @@ -343,11 +325,14 @@ public class StringUtils { try { randomString(charBuffer, random, alphabet, numRandomChars); } catch (IOException e) { - // This should never happen if we calcuate the buffer size correctly. + // This should never happen if we calculate the buffer size correctly. throw new AssertionError(e); } - return charBuffer.flip().toString(); + // Workaround for Android API not matching Java >=9 API. + // See https://issuetracker.google.com/issues/369219141 + ((java.nio.Buffer) charBuffer).flip(); + return charBuffer.toString(); } private static void randomString(Appendable appendable, Random random, char[] alphabet, int numRandomChars) @@ -479,7 +464,7 @@ public class StringUtils { appendTo(collection, ", ", sb); } - public static void appendTo(Collection collection, StringBuilder sb, + public static void appendTo(Collection collection, StringBuilder sb, Consumer appendFunction) { appendTo(collection, ", ", sb, appendFunction); } @@ -488,7 +473,7 @@ public class StringUtils { appendTo(collection, delimiter, sb, o -> sb.append(o)); } - public static void appendTo(Collection collection, String delimiter, StringBuilder sb, + public static void appendTo(Collection collection, String delimiter, StringBuilder sb, Consumer appendFunction) { for (Iterator it = collection.iterator(); it.hasNext();) { O cs = it.next(); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java index c1af8ba61..47469f721 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright 2014-2020 Florian Schmaus + * Copyright 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,52 +68,6 @@ public class TLSUtils { return builder; } - /** - * Enable only TLS. Connections created with the given ConnectionConfiguration will only support TLS. - *

      - * According to the Encrypted - * XMPP Manifesto, TLSv1.2 shall be deployed, providing fallback support for SSLv3 and - * TLSv1.1. This method goes one step beyond and upgrades the handshake to use TLSv1 or better. - * This method requires the underlying OS to support all of TLSv1.2 , 1.1 and 1.0. - *

      - * - * @param builder the configuration builder to apply this setting to - * @param Type of the ConnectionConfiguration builder. - * - * @return the given builder - * @deprecated use {@link #setEnabledTlsProtocolsToRecommended(org.jivesoftware.smack.ConnectionConfiguration.Builder)} instead. - */ - // TODO: Remove in Smack 4.5. - @Deprecated - public static > B setTLSOnly(B builder) { - builder.setEnabledSSLProtocols(new String[] { PROTO_TLSV1_2, PROTO_TLSV1_1, PROTO_TLSV1 }); - return builder; - } - - /** - * Enable only TLS and SSLv3. Connections created with the given ConnectionConfiguration will - * only support TLS and SSLv3. - *

      - * According to the Encrypted - * XMPP Manifesto, TLSv1.2 shall be deployed, providing fallback support for SSLv3 and - * TLSv1.1. - *

      - * - * @param builder the configuration builder to apply this setting to - * @param Type of the ConnectionConfiguration builder. - * - * @return the given builder - * @deprecated use {@link #setEnabledTlsProtocolsToRecommended(org.jivesoftware.smack.ConnectionConfiguration.Builder)} instead. - */ - // TODO: Remove in Smack 4.5. - @Deprecated - public static > B setSSLv3AndTLSOnly(B builder) { - builder.setEnabledSSLProtocols(new String[] { PROTO_TLSV1_2, PROTO_TLSV1_1, PROTO_TLSV1, PROTO_SSL3 }); - return builder; - } - /** * Accept all TLS certificates. *

      diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java b/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java index 3ecb67877..5c327157b 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java @@ -1,6 +1,6 @@ /** * - * Copyright 2014-2023 Florian Schmaus + * Copyright 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,6 +47,7 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element { this(pe, null); } + @SuppressWarnings("this-escape") public XmlStringBuilder(NamedElement e) { this(); halfOpenElement(e.getElementName()); @@ -56,6 +57,7 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element { this(element.getElementName(), element.getNamespace(), element.getLanguage(), enclosingXmlEnvironment); } + @SuppressWarnings("this-escape") public XmlStringBuilder(String elementName, String xmlNs, String xmlLang, XmlEnvironment enclosingXmlEnvironment) { sb = new LazyStringBuilder(); halfOpenElement(elementName); @@ -138,20 +140,6 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element { return this; } - /** - * Deprecated. - * - * @param element deprecated. - * @return deprecated. - * @deprecated use {@link #append(Element)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public XmlStringBuilder element(Element element) { - assert element != null; - return append(element.toXML()); - } - public XmlStringBuilder optElement(String name, String content) { if (content != null) { element(name, content); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/XmppElementUtil.java b/smack-core/src/main/java/org/jivesoftware/smack/util/XmppElementUtil.java index 5639a7e5f..7ffe2f2eb 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/XmppElementUtil.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/XmppElementUtil.java @@ -71,6 +71,7 @@ public class XmppElementUtil { return qname; } + @SuppressWarnings("MixedMutabilityReturnType") public static List getElementsFrom( MultiMap elementMap, Class extensionElementClass) { QName qname = XmppElementUtil.getQNameFor(extensionElementClass); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/dns/package-info.java b/smack-core/src/main/java/org/jivesoftware/smack/util/dns/package-info.java index f06cdab84..1fb7e9b3b 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/dns/package-info.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/dns/package-info.java @@ -61,8 +61,8 @@ *

      *

      Best Practices

      *

      - * We recommend that applications using Smack's DNSSEC API do not ask the user if DNSSEC is avaialble. Instead they - * should check for DNSSEC suport on every connection attempt. Once DNSSEC support has been discovered, the application + * We recommend that applications using Smack's DNSSEC API do not ask the user if DNSSEC is available. Instead they + * should check for DNSSEC support on every connection attempt. Once DNSSEC support has been discovered, the application * should use the `needsDnssec` mode for all future connection attempts. The same scheme can be applied when using DANE. * This approach is similar to the scheme established by to HTTP Strict * Transport Security" (HSTS, RFC 6797. diff --git a/smack-core/src/test/java/org/jivesoftware/smack/packet/IqTest.java b/smack-core/src/test/java/org/jivesoftware/smack/packet/IqTest.java index b8f192bd7..2101daca4 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/packet/IqTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/packet/IqTest.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2023 Florian Schmaus + * Copyright © 2023-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,13 @@ package org.jivesoftware.smack.packet; import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; +import org.jivesoftware.smack.test.util.SmackTestUtil; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smack.xml.XmlPullParser; + import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; public class IqTest { @@ -35,4 +41,36 @@ public class IqTest { assertXmlSimilar(expected, errorIq.toXML()); } + @ParameterizedTest + @EnumSource(SmackTestUtil.XmlPullParserKind.class) + public void testIqWithXmlns(SmackTestUtil.XmlPullParserKind parserKind) throws Exception { + final String iqXml = "" + + "" + + "foo@tigase.mydomain.org/myresource" + + "" + + ""; + final String xml = + "" + + iqXml + + ""; + + XmlPullParser parser = SmackTestUtil.getParserFor(xml, "iq", parserKind); + IQ iq = PacketParserUtils.parseIQ(parser); + assertXmlSimilar(iqXml, iq.toXML()); + } + + @ParameterizedTest + @EnumSource(SmackTestUtil.XmlPullParserKind.class) + public void testUnparsedIq(SmackTestUtil.XmlPullParserKind parserKind) throws Exception { + final String iqXml = "" + + "" + + ""; + final String expected = "" + + "<query xmlns='jabber:iq:version'/>" + + ""; + + XmlPullParser parser = SmackTestUtil.getParserFor(iqXml, "iq", parserKind); + IQ iq = PacketParserUtils.parseIQ(parser); + assertXmlSimilar(expected, iq.toXML()); + } } diff --git a/smack-core/src/testFixtures/java/org/jivesoftware/smack/DummyConnection.java b/smack-core/src/testFixtures/java/org/jivesoftware/smack/DummyConnection.java index 7e105d58b..320bb1f9e 100644 --- a/smack-core/src/testFixtures/java/org/jivesoftware/smack/DummyConnection.java +++ b/smack-core/src/testFixtures/java/org/jivesoftware/smack/DummyConnection.java @@ -1,6 +1,6 @@ /** * - * Copyright 2010 Jive Software, 2022 Florian Schmaus. + * Copyright 2010 Jive Software, 2022-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,9 +39,9 @@ import org.jxmpp.stringprep.XmppStringprepException; * A dummy implementation of {@link XMPPConnection}, intended to be used during * unit tests. * - * Instances store any packets that are delivered to be send using the + * Instances store any packets that are delivered to be sent using the * {@link #sendStanza(Stanza)} method in a blocking queue. The content of this queue - * can be inspected using {@link #getSentPacket()}. Typically these queues are + * can be inspected using {@link #getSentPacket()}. Typically, these queues are * used to retrieve a message that was generated by the client. * * Packets that should be processed by the client to simulate a received stanza @@ -82,6 +82,7 @@ public class DummyConnection extends AbstractXMPPConnection { } } + @SuppressWarnings("this-escape") public DummyConnection(DummyConnectionConfiguration configuration) { super(configuration); @@ -91,6 +92,7 @@ public class DummyConnection extends AbstractXMPPConnection { user = getUserJid(); } + @SuppressWarnings("JavaUtilDate") @Override protected void connectInternal() { connected = true; @@ -162,6 +164,7 @@ public class DummyConnection extends AbstractXMPPConnection { * @param

      the top level stream element class. * @return a sent packet. */ + @SuppressWarnings("TypeParameterUnusedInFormals") public

      P getSentPacket() { return getSentPacket(5 * 60); } @@ -176,7 +179,7 @@ public class DummyConnection extends AbstractXMPPConnection { * @param

      the top level stream element class. * @return a sent packet. */ - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) public

      P getSentPacket(int wait) { try { return (P) queue.poll(wait, TimeUnit.SECONDS); diff --git a/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/MemoryLeakTestUtil.java b/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/MemoryLeakTestUtil.java index 6746e8f13..1eccfd75a 100644 --- a/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/MemoryLeakTestUtil.java +++ b/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/MemoryLeakTestUtil.java @@ -41,7 +41,7 @@ import org.jxmpp.stringprep.XmppStringprepException; * Note that this test is based on the assumption that it is possible to trigger a full garbage collection run, which is * not the case. See also this * stackoverflow - * question. Hence the {@link #triggerGarbageCollection()} method defined in this class is not portable and depends + * question. Hence, the {@link #triggerGarbageCollection()} method defined in this class is not portable and depends * on implementation depended Java Virtual Machine behavior. *

      * diff --git a/smack-debug-slf4j/build.gradle b/smack-debug-slf4j/build.gradle index 35d904a7e..71eae05b4 100644 --- a/smack-debug-slf4j/build.gradle +++ b/smack-debug-slf4j/build.gradle @@ -1,3 +1,7 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' +} + description = """\ Smack slf4j debugger. Inspect the exchanged XMPP stanzas. diff --git a/smack-debug-slf4j/src/main/java/org/jivesoftware/smackx/package-info.java b/smack-debug-slf4j/src/main/java/org/jivesoftware/smackx/package-info.java index 7ebe86244..bce3b6064 120000 --- a/smack-debug-slf4j/src/main/java/org/jivesoftware/smackx/package-info.java +++ b/smack-debug-slf4j/src/main/java/org/jivesoftware/smackx/package-info.java @@ -1 +1 @@ -../../../../../../../smack-java8-full/src/main/java/org/jivesoftware/smackx/package-info.java \ No newline at end of file +../../../../../../../smack-java11-full/src/main/java/org/jivesoftware/smackx/package-info.java \ No newline at end of file diff --git a/smack-debug/build.gradle b/smack-debug/build.gradle index 959caff4f..7c9fdc0f1 100644 --- a/smack-debug/build.gradle +++ b/smack-debug/build.gradle @@ -1,3 +1,7 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' +} + description = """\ Smack GUI debugger. Inspect the exchanged XMPP stanzas.""" diff --git a/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java b/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java index 40d2e5263..e57b4aaaa 100644 --- a/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java +++ b/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java @@ -160,6 +160,7 @@ public class EnhancedDebugger extends SmackDebugger { private ReaderListener readerListener; private WriterListener writerListener; + @SuppressWarnings("JavaUtilDate") private Date creationTime = new Date(); // Statistics variables @@ -177,6 +178,7 @@ public class EnhancedDebugger extends SmackDebugger { JTabbedPane tabbedPane; + @SuppressWarnings("this-escape") public EnhancedDebugger(XMPPConnection connection) { super(connection); @@ -756,6 +758,7 @@ public class EnhancedDebugger extends SmackDebugger { * @param dateFormatter the SimpleDateFormat to use to format Dates * @param packet the read stanza to add to the table */ + @SuppressWarnings("JavaUtilDate") private void addReadPacketToTable(final SimpleDateFormat dateFormatter, final TopLevelStreamElement packet) { SwingUtilities.invokeLater(new Runnable() { @Override @@ -827,6 +830,7 @@ public class EnhancedDebugger extends SmackDebugger { * @param dateFormatter the SimpleDateFormat to use to format Dates * @param packet the sent stanza to add to the table */ + @SuppressWarnings("JavaUtilDate") private void addSentPacketToTable(final SimpleDateFormat dateFormatter, final TopLevelStreamElement packet) { SwingUtilities.invokeLater(new Runnable() { @Override diff --git a/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java b/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java index 5755207d8..cadcb0b53 100644 --- a/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java +++ b/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java @@ -204,7 +204,7 @@ public final class EnhancedDebuggerWindow { * Creates the main debug window that provides information about Smack and also shows * a tab panel for each connection that is being debugged. */ - @SuppressWarnings({ "rawtypes", "unchecked" }) + @SuppressWarnings({ "rawtypes", "unchecked", "JdkObsolete" }) private void createDebug() { frame = new JFrame("Smack Debug Window"); diff --git a/smack-debug/src/main/java/org/jivesoftware/smackx/package-info.java b/smack-debug/src/main/java/org/jivesoftware/smackx/package-info.java index 7ebe86244..bce3b6064 120000 --- a/smack-debug/src/main/java/org/jivesoftware/smackx/package-info.java +++ b/smack-debug/src/main/java/org/jivesoftware/smackx/package-info.java @@ -1 +1 @@ -../../../../../../../smack-java8-full/src/main/java/org/jivesoftware/smackx/package-info.java \ No newline at end of file +../../../../../../../smack-java11-full/src/main/java/org/jivesoftware/smackx/package-info.java \ No newline at end of file diff --git a/smack-examples/build.gradle b/smack-examples/build.gradle index 7592441a5..244040dd7 100644 --- a/smack-examples/build.gradle +++ b/smack-examples/build.gradle @@ -1,3 +1,7 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' +} + description = """\ Examples and test applications for Smack""" diff --git a/smack-examples/src/main/java/org/igniterealtime/smack/examples/OmemoClient.java b/smack-examples/src/main/java/org/igniterealtime/smack/examples/OmemoClient.java index b247c77b2..9c8652107 100644 --- a/smack-examples/src/main/java/org/igniterealtime/smack/examples/OmemoClient.java +++ b/smack-examples/src/main/java/org/igniterealtime/smack/examples/OmemoClient.java @@ -185,7 +185,7 @@ public class OmemoClient { BareJid contact = JidCreate.bareFrom(com[1]); - HashMap devices; + Map devices; try { devices = omemoManager.getActiveFingerprints(contact); } catch (CorruptedOmemoKeyException | CannotEstablishOmemoSessionException | SmackException.NoResponseException e) { diff --git a/smack-examples/src/main/java/org/igniterealtime/smack/examples/XmppConnectionTool.java b/smack-examples/src/main/java/org/igniterealtime/smack/examples/XmppConnectionTool.java new file mode 100644 index 000000000..c44adb157 --- /dev/null +++ b/smack-examples/src/main/java/org/igniterealtime/smack/examples/XmppConnectionTool.java @@ -0,0 +1,82 @@ +/** + * + * Copyright 2024 Florian Schmaus. + * + * This file is part of smack-examples. + * + * smack-examples is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.igniterealtime.smack.examples; + +import java.io.IOException; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.SmackException.NoResponseException; +import org.jivesoftware.smack.SmackException.NotConnectedException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.XMPPException.XMPPErrorException; +import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection; +import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration; +import org.jivesoftware.smack.debugger.ConsoleDebugger; +import org.jivesoftware.smack.debugger.SmackDebuggerFactory; +import org.jivesoftware.smackx.omemo.util.OmemoConstants; +import org.jivesoftware.smackx.pep.PepManager; +import org.jivesoftware.smackx.pubsub.PubSubManager; + +import org.jxmpp.stringprep.XmppStringprepException; + +public class XmppConnectionTool { + + public final ModularXmppClientToServerConnection connection; + + public XmppConnectionTool(ModularXmppClientToServerConnection connection) { + this.connection = connection; + } + + public boolean purgeOmemoInformation() + throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + PepManager pepManager = PepManager.getInstanceFor(connection); + PubSubManager pepPubSubManager = pepManager.getPepPubSubManager(); + + // TODO: Also delete "bundles" nodes. + return pepPubSubManager.deleteNode(OmemoConstants.PEP_NODE_DEVICE_LIST); + } + + public static XmppConnectionTool of(String jid, String password, boolean debug) + throws XMPPException, SmackException, IOException, InterruptedException { + ModularXmppClientToServerConnection connection = createConnectionFor(jid, password, debug); + connection.connect().login(); + return new XmppConnectionTool(connection); + } + + public static ModularXmppClientToServerConnection createConnectionFor(String jid, String password, boolean debug) + throws XmppStringprepException { + + final SmackDebuggerFactory smackDebuggerFactory; + if (debug) { + smackDebuggerFactory = ConsoleDebugger.Factory.INSTANCE; + } else { + smackDebuggerFactory = null; + } + + ModularXmppClientToServerConnectionConfiguration.Builder configurationBuilder = ModularXmppClientToServerConnectionConfiguration + .builder().setXmppAddressAndPassword(jid, password).setDebuggerFactory(smackDebuggerFactory); + + ModularXmppClientToServerConnectionConfiguration configuration = configurationBuilder.build(); + + ModularXmppClientToServerConnection connection = new ModularXmppClientToServerConnection(configuration); + return connection; + } +} diff --git a/smack-examples/src/main/java/org/igniterealtime/smack/examples/XmppTools.java b/smack-examples/src/main/java/org/igniterealtime/smack/examples/XmppTools.java index f262102c2..70bb9f1f2 100644 --- a/smack-examples/src/main/java/org/igniterealtime/smack/examples/XmppTools.java +++ b/smack-examples/src/main/java/org/igniterealtime/smack/examples/XmppTools.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2021 Florian Schmaus + * Copyright 2016-2024 Florian Schmaus * * This file is part of smack-examples. * @@ -136,6 +136,7 @@ public class XmppTools { // CHECKSTYLE:ON } + @SuppressWarnings("JavaUtilDate") public static void sendItsAlive(String to, XMPPConnection connection) throws XmppStringprepException, NotConnectedException, InterruptedException { if (to == null) { diff --git a/smack-experimental/build.gradle b/smack-experimental/build.gradle index 9e945e0d5..7484d56f8 100644 --- a/smack-experimental/build.gradle +++ b/smack-experimental/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} + description = """\ Smack experimental extensions. Classes and methods for XEPs that are in status 'experimental' or that should diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/packet/Carbon.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/packet/Carbon.java index 8f303cdff..77ef518f6 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/packet/Carbon.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/packet/Carbon.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014 Florian Schmaus + * Copyright © 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ public class Carbon { public static class Enable extends SimpleIQ { public static final String ELEMENT = "enable"; + @SuppressWarnings("this-escape") public Enable() { super(ELEMENT, NAMESPACE); setType(Type.set); @@ -37,6 +38,7 @@ public class Carbon { public static class Disable extends SimpleIQ { public static final String ELEMENT = "disable"; + @SuppressWarnings("this-escape") public Disable() { super(ELEMENT, NAMESPACE); setType(Type.set); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/element/DnsIq.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/element/DnsIq.java index 77c626eae..590906549 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/element/DnsIq.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/element/DnsIq.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019 Florian Schmaus + * Copyright 2019-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ public class DnsIq extends IQ { this(new DnsMessage(dnsMessage)); } + @SuppressWarnings("this-escape") public DnsIq(DnsMessage dnsQuery, Jid to) { this(dnsQuery); setTo(to); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/hashes/HashManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/hashes/HashManager.java index b3f0c3db4..68aa79ba7 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/hashes/HashManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/hashes/HashManager.java @@ -47,7 +47,7 @@ import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.hashes.element.HashElement; /** - * Manager that can be used to determine support for hash functions. By default the Manager announces support for + * Manager that can be used to determine support for hash functions. By default,the Manager announces support for * XEP-0300, as well as for the recommended set of hash algorithms. Those contain SHA256, SHA384, SHA512, SHA3-256, * SHA3-384, SHA3-512, BLAKE2B256, BLAKE2B384 and BLAKE2B512. Those algorithms got recommended here: * https://xmpp.org/extensions/xep-0300.html#recommendations. diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/hoxt/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/hoxt/package-info.java index 1b357afa8..9ca583637 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/hoxt/package-info.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/hoxt/package-info.java @@ -103,7 +103,7 @@ * AbstractHttpOverXmpp.Data data = new AbstractHttpOverXmpp.Data(child); * * // create request - * HttpOverXmppReq req = HttpOverXmppReq.buider() + * HttpOverXmppReq req = HttpOverXmppReq.builder() * .setMethod(HttpMethod.POST) * .setResource("/mailbox") * .setHeaders(headers) diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/Slot.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/Slot.java index 274e59bc0..48c61bec7 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/Slot.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/Slot.java @@ -46,6 +46,7 @@ public class Slot extends IQ { this(putUrl, getUrl, headers, NAMESPACE); } + @SuppressWarnings("this-escape") protected Slot(URL putUrl, URL getUrl, Map headers, String namespace) { super(ELEMENT, namespace); setType(Type.result); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/SlotRequest.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/SlotRequest.java index 961fc27e1..0fa8b484c 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/SlotRequest.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/SlotRequest.java @@ -53,6 +53,7 @@ public class SlotRequest extends IQ { this(uploadServiceAddress, filename, size, contentType, NAMESPACE); } + @SuppressWarnings("this-escape") protected SlotRequest(DomainBareJid uploadServiceAddress, String filename, long size, String contentType, String namespace) { super(ELEMENT, namespace); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/control/element/IoTSetRequest.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/control/element/IoTSetRequest.java index cd6d0b553..a3dad0268 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/control/element/IoTSetRequest.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/control/element/IoTSetRequest.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2016-2017 Florian Schmaus + * Copyright © 2016-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ public class IoTSetRequest extends IQ { private final Collection setData; + @SuppressWarnings("this-escape") public IoTSetRequest(Collection setData) { super(ELEMENT, NAMESPACE); setType(Type.set); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/data/element/IoTDataReadOutAccepted.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/data/element/IoTDataReadOutAccepted.java index 20d0a125d..279476cd2 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/data/element/IoTDataReadOutAccepted.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/data/element/IoTDataReadOutAccepted.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2016 Florian Schmaus + * Copyright © 2016-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ public class IoTDataReadOutAccepted extends IQ { private final boolean queued; + @SuppressWarnings("this-escape") public IoTDataReadOutAccepted(int seqNr, boolean queued) { super(ELEMENT, NAMESPACE); this.seqNr = seqNr; @@ -37,6 +38,7 @@ public class IoTDataReadOutAccepted extends IQ { setType(Type.result); } + @SuppressWarnings("this-escape") public IoTDataReadOutAccepted(IoTDataRequest dataRequest) { this(dataRequest.getSequenceNr(), false); setStanzaId(dataRequest.getStanzaId()); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/data/element/IoTFieldsExtension.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/data/element/IoTFieldsExtension.java index cb280745f..721b1b657 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/data/element/IoTFieldsExtension.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/data/element/IoTFieldsExtension.java @@ -83,6 +83,7 @@ public class IoTFieldsExtension implements ExtensionElement { return xml; } + @SuppressWarnings("JavaUtilDate") public static IoTFieldsExtension buildFor(int seqNr, boolean done, NodeInfo nodeInfo, List data) { TimestampElement timestampElement = new TimestampElement(new Date(), data); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/discovery/element/IoTUnregister.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/discovery/element/IoTUnregister.java index bdc536f97..3b510e55f 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/discovery/element/IoTUnregister.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/discovery/element/IoTUnregister.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016 Florian Schmaus + * Copyright 2016-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ public class IoTUnregister extends IQ { private final NodeInfo nodeInfo; + @SuppressWarnings("this-escape") public IoTUnregister(NodeInfo nodeInfo) { super(ELEMENT, NAMESPACE); this.nodeInfo = nodeInfo; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/package-info.java index 0b3803a4f..c4b261a27 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/package-info.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/package-info.java @@ -146,7 +146,7 @@ *
        * 
        * IoTDiscoveryManager iotDiscoveryManager = IoTDiscoveryManager.getInstanceFor(connection);
      - * iotDiscovyerManager.registerThing(thing);
      + * iotDiscoveryManager.registerThing(thing);
        * 
        * 
      *

      @@ -162,7 +162,7 @@ * Things can usually only be used by other things if they are friends. Since a * thing normally can't decide on its own if an incoming friendship request * should be granted or not, we can delegate this decision to a provisioning - * service. Smack provides the `IoTProvisinoManager` to deal with friendship and + * service. Smack provides the `IoTProvisionManager` to deal with friendship and * provisioning. *

      *

      diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/element/ClearCache.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/element/ClearCache.java index fe98b32b3..d17e46c94 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/element/ClearCache.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/element/ClearCache.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2016 Florian Schmaus + * Copyright © 2016-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ public class ClearCache extends SimpleIQ { public static final String ELEMENT = "clearCache"; public static final String NAMESPACE = Constants.IOT_PROVISIONING_NAMESPACE; + @SuppressWarnings("this-escape") public ClearCache() { super(ELEMENT, NAMESPACE); // IQs are always of type 'get' (XEP-0324 § 3.5.1, see also the XEPs history remarks) diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/element/ClearCacheResponse.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/element/ClearCacheResponse.java index 1c229d153..1abfb731c 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/element/ClearCacheResponse.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/element/ClearCacheResponse.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2016 Florian Schmaus + * Copyright © 2016-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,12 +23,14 @@ public class ClearCacheResponse extends SimpleIQ { public static final String ELEMENT = "clearCacheResponse"; public static final String NAMESPACE = Constants.IOT_PROVISIONING_NAMESPACE; + @SuppressWarnings("this-escape") public ClearCacheResponse() { super(ELEMENT, NAMESPACE); // IQs are always of type 'result' (XEP-0324 § 3.5.1, see also the XEPs history remarks) setType(Type.result); } + @SuppressWarnings("this-escape") public ClearCacheResponse(ClearCache clearCacheRequest) { this(); setStanzaId(clearCacheRequest.getStanzaId()); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransferChild.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransferChild.java index b4e9021ec..cab86e550 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransferChild.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransferChild.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Paul Schaub, 2019 Florian Schmaus + * Copyright 2017 Paul Schaub, 2019-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -167,6 +167,7 @@ public class JingleFileTransferChild implements JingleContentDescriptionChildEle return new JingleFileTransferChild(date, desc, hash, mediaType, name, size, range); } + @SuppressWarnings("JavaUtilDate") public Builder setFile(File file) { return setDate(new Date(file.lastModified())) .setName(file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf("/") + 1)) diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java index 985ca8b48..6ce58bdea 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2017-2023 Florian Schmaus, 2016-2017 Fernando Ramirez + * Copyright © 2017-2024 Florian Schmaus, 2016-2017 Fernando Ramirez * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -387,6 +387,7 @@ public final class MamManager extends Manager { return this; } + @SuppressWarnings("JavaUtilDate") public Builder limitResultsSince(Date start) { if (start == null) { return this; @@ -415,6 +416,7 @@ public final class MamManager extends Manager { return this; } + @SuppressWarnings("JavaUtilDate") public Builder limitResultsBefore(Date end) { if (end == null) { return this; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java index e5898f4c3..1a5535135 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamPrefsIQ.java @@ -81,6 +81,7 @@ public class MamPrefsIQ extends IQ { * @param neverJids TODO javadoc me please * @param defaultBehavior TODO javadoc me please */ + @SuppressWarnings("this-escape") public MamPrefsIQ(MamVersion version, List alwaysJids, List neverJids, DefaultBehavior defaultBehavior) { super(ELEMENT, version.getNamespace()); setType(Type.set); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java index 3909b077c..17b8a5cae 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java @@ -45,6 +45,7 @@ public class MamQueryIQ extends IQ { * @param version TODO javadoc me please * @param queryId TODO javadoc me please */ + @SuppressWarnings("this-escape") public MamQueryIQ(MamVersion version, String queryId) { this(version, queryId, null, null); setType(IQ.Type.get); @@ -79,6 +80,7 @@ public class MamQueryIQ extends IQ { * @param node TODO javadoc me please * @param dataForm TODO javadoc me please */ + @SuppressWarnings("this-escape") public MamQueryIQ(MamVersion version, String queryId, String node, DataForm dataForm) { super(ELEMENT, version.getNamespace()); this.queryId = queryId; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/message_markup/element/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/message_markup/element/package-info.java index 5246c33ff..f1d6e981e 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/message_markup/element/package-info.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/message_markup/element/package-info.java @@ -21,7 +21,7 @@ *

      Usage

      *

      * The most important class is the {@link org.jivesoftware.smackx.message_markup.element.MarkupElement} class, which - * contains a Builder to construct message markup.. + * contains a Builder to construct message markup. *

      *

      * To start creating a Message Markup Extension, call diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MUCLightRoomConfiguration.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MUCLightRoomConfiguration.java index aab822087..7f0595871 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MUCLightRoomConfiguration.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MUCLightRoomConfiguration.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.muclight; -import java.util.HashMap; +import java.util.Map; /** * MUC Light room configuration class. @@ -28,7 +28,7 @@ public class MUCLightRoomConfiguration { private final String roomName; private final String subject; - private final HashMap customConfigs; + private final Map customConfigs; /** * MUC Light room configuration model constructor. @@ -37,7 +37,7 @@ public class MUCLightRoomConfiguration { * @param subject TODO javadoc me please * @param customConfigs TODO javadoc me please */ - public MUCLightRoomConfiguration(String roomName, String subject, HashMap customConfigs) { + public MUCLightRoomConfiguration(String roomName, String subject, Map customConfigs) { this.roomName = roomName; this.subject = subject; this.customConfigs = customConfigs; @@ -66,7 +66,7 @@ public class MUCLightRoomConfiguration { * * @return the custom configurations of the room. */ - public HashMap getCustomConfigs() { + public Map getCustomConfigs() { return customConfigs; } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MUCLightRoomInfo.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MUCLightRoomInfo.java index b41066d54..532f64bf4 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MUCLightRoomInfo.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MUCLightRoomInfo.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.muclight; -import java.util.HashMap; +import java.util.Map; import org.jxmpp.jid.Jid; @@ -30,7 +30,7 @@ public class MUCLightRoomInfo { private final String version; private final Jid room; private final MUCLightRoomConfiguration configuration; - private final HashMap occupants; + private final Map occupants; /** * MUC Light room info model constructor. @@ -41,7 +41,7 @@ public class MUCLightRoomInfo { * @param occupants TODO javadoc me please */ public MUCLightRoomInfo(String version, Jid roomJid, MUCLightRoomConfiguration configuration, - HashMap occupants) { + Map occupants) { this.version = version; this.room = roomJid; this.configuration = configuration; @@ -80,7 +80,7 @@ public class MUCLightRoomInfo { * * @return the occupants of the room. */ - public HashMap getOccupants() { + public Map getOccupants() { return occupants; } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MultiUserChatLight.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MultiUserChatLight.java index 5ebd11197..58e5b367d 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MultiUserChatLight.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MultiUserChatLight.java @@ -18,6 +18,7 @@ package org.jivesoftware.smackx.muclight; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; @@ -181,21 +182,6 @@ public class MultiUserChatLight { ; } - /** - * Sends a Message to the chat room. - * - * @param message TODO javadoc me please - * the message. - * @throws NotConnectedException if the XMPP connection is not connected. - * @throws InterruptedException if the calling thread was interrupted. - * @deprecated use {@link #sendMessage(MessageBuilder)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public void sendMessage(Message message) throws NotConnectedException, InterruptedException { - sendMessage(message.asBuilder()); - } - /** * Sends a Message to the chat room. * @@ -294,7 +280,7 @@ public class MultiUserChatLight { * @param occupants TODO javadoc me please * @throws Exception TODO javadoc me please */ - public void create(String roomName, String subject, HashMap customConfigs, List occupants) + public void create(String roomName, String subject, Map customConfigs, List occupants) throws Exception { MUCLightCreateIQ createMUCLightIQ = new MUCLightCreateIQ(room, roomName, occupants); @@ -328,7 +314,7 @@ public class MultiUserChatLight { * @throws XMPPErrorException if there was an XMPP error returned. */ public void leave() throws NotConnectedException, InterruptedException, NoResponseException, XMPPErrorException { - HashMap affiliations = new HashMap<>(); + Map affiliations = new HashMap<>(); affiliations.put(connection.getUser(), MUCLightAffiliation.none); MUCLightChangeAffiliationsIQ changeAffiliationsIQ = new MUCLightChangeAffiliationsIQ(room, affiliations); @@ -417,7 +403,7 @@ public class MultiUserChatLight { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public HashMap getAffiliations(String version) + public Map getAffiliations(String version) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { MUCLightGetAffiliationsIQ mucLightGetAffiliationsIQ = new MUCLightGetAffiliationsIQ(room, version); @@ -436,7 +422,7 @@ public class MultiUserChatLight { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public HashMap getAffiliations() + public Map getAffiliations() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { return getAffiliations(null); } @@ -450,7 +436,7 @@ public class MultiUserChatLight { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void changeAffiliations(HashMap affiliations) + public void changeAffiliations(Map affiliations) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { MUCLightChangeAffiliationsIQ changeAffiliationsIQ = new MUCLightChangeAffiliationsIQ(room, affiliations); connection.sendIqRequestAndWaitForResponse(changeAffiliationsIQ); @@ -513,7 +499,7 @@ public class MultiUserChatLight { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void setRoomConfigs(HashMap customConfigs) + public void setRoomConfigs(Map customConfigs) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { setRoomConfigs(null, customConfigs); } @@ -528,7 +514,7 @@ public class MultiUserChatLight { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void setRoomConfigs(String roomName, HashMap customConfigs) + public void setRoomConfigs(String roomName, Map customConfigs) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { MUCLightSetConfigsIQ mucLightSetConfigIQ = new MUCLightSetConfigsIQ(room, roomName, customConfigs); connection.sendIqRequestAndWaitForResponse(mucLightSetConfigIQ); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MultiUserChatLightManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MultiUserChatLightManager.java index d153d499d..58f030499 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MultiUserChatLightManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MultiUserChatLightManager.java @@ -274,7 +274,7 @@ public final class MultiUserChatLightManager extends Manager { sendBlockRooms(mucLightService, rooms); } - private void sendBlockRooms(DomainBareJid mucLightService, HashMap rooms) + private void sendBlockRooms(DomainBareJid mucLightService, Map rooms) throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException { MUCLightBlockingIQ mucLightBlockingIQ = new MUCLightBlockingIQ(rooms, null); mucLightBlockingIQ.setType(IQ.Type.set); @@ -318,7 +318,7 @@ public final class MultiUserChatLightManager extends Manager { sendBlockUsers(mucLightService, users); } - private void sendBlockUsers(DomainBareJid mucLightService, HashMap users) + private void sendBlockUsers(DomainBareJid mucLightService, Map users) throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException { MUCLightBlockingIQ mucLightBlockingIQ = new MUCLightBlockingIQ(null, users); mucLightBlockingIQ.setType(IQ.Type.set); @@ -362,7 +362,7 @@ public final class MultiUserChatLightManager extends Manager { sendUnblockRooms(mucLightService, rooms); } - private void sendUnblockRooms(DomainBareJid mucLightService, HashMap rooms) + private void sendUnblockRooms(DomainBareJid mucLightService, Map rooms) throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException { MUCLightBlockingIQ mucLightBlockingIQ = new MUCLightBlockingIQ(rooms, null); mucLightBlockingIQ.setType(IQ.Type.set); @@ -406,7 +406,7 @@ public final class MultiUserChatLightManager extends Manager { sendUnblockUsers(mucLightService, users); } - private void sendUnblockUsers(DomainBareJid mucLightService, HashMap users) + private void sendUnblockUsers(DomainBareJid mucLightService, Map users) throws NoResponseException, XMPPErrorException, InterruptedException, NotConnectedException { MUCLightBlockingIQ mucLightBlockingIQ = new MUCLightBlockingIQ(null, users); mucLightBlockingIQ.setType(IQ.Type.set); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightAffiliationsIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightAffiliationsIQ.java index c9e0ead62..047c7af50 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightAffiliationsIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightAffiliationsIQ.java @@ -16,7 +16,6 @@ */ package org.jivesoftware.smackx.muclight.element; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -40,7 +39,7 @@ public class MUCLightAffiliationsIQ extends IQ { public static final String NAMESPACE = MultiUserChatLight.NAMESPACE + MultiUserChatLight.AFFILIATIONS; private final String version; - private HashMap affiliations; + private Map affiliations; /** * MUC Light affiliations response IQ constructor. @@ -48,7 +47,8 @@ public class MUCLightAffiliationsIQ extends IQ { * @param version TODO javadoc me please * @param affiliations TODO javadoc me please */ - public MUCLightAffiliationsIQ(String version, HashMap affiliations) { + @SuppressWarnings("this-escape") + public MUCLightAffiliationsIQ(String version, Map affiliations) { super(ELEMENT, NAMESPACE); this.version = version; this.affiliations = affiliations; @@ -82,7 +82,7 @@ public class MUCLightAffiliationsIQ extends IQ { * * @return the affiliations of the room */ - public HashMap getAffiliations() { + public Map getAffiliations() { return affiliations; } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightBlockingIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightBlockingIQ.java index f3334b651..fa6264ff0 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightBlockingIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightBlockingIQ.java @@ -16,7 +16,6 @@ */ package org.jivesoftware.smackx.muclight.element; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -38,8 +37,8 @@ public class MUCLightBlockingIQ extends IQ { public static final String ELEMENT = QUERY_ELEMENT; public static final String NAMESPACE = MultiUserChatLight.NAMESPACE + MultiUserChatLight.BLOCKING; - private final HashMap rooms; - private final HashMap users; + private final Map rooms; + private final Map users; /** * MUC Light blocking IQ constructor. @@ -47,7 +46,7 @@ public class MUCLightBlockingIQ extends IQ { * @param rooms TODO javadoc me please * @param users TODO javadoc me please */ - public MUCLightBlockingIQ(HashMap rooms, HashMap users) { + public MUCLightBlockingIQ(Map rooms, Map users) { super(ELEMENT, NAMESPACE); this.rooms = rooms; this.users = users; @@ -58,7 +57,7 @@ public class MUCLightBlockingIQ extends IQ { * * @return the rooms JIDs with booleans (true if allow, false if deny) */ - public HashMap getRooms() { + public Map getRooms() { return rooms; } @@ -67,7 +66,7 @@ public class MUCLightBlockingIQ extends IQ { * * @return the users JIDs with booleans (true if allow, false if deny) */ - public HashMap getUsers() { + public Map getUsers() { return users; } @@ -86,7 +85,7 @@ public class MUCLightBlockingIQ extends IQ { return xml; } - private static void parseBlocking(IQChildElementXmlStringBuilder xml, HashMap map, boolean isRoom) { + private static void parseBlocking(IQChildElementXmlStringBuilder xml, Map map, boolean isRoom) { Iterator> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = it.next(); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightChangeAffiliationsIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightChangeAffiliationsIQ.java index 494a7a4d7..e472c84e1 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightChangeAffiliationsIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightChangeAffiliationsIQ.java @@ -16,7 +16,6 @@ */ package org.jivesoftware.smackx.muclight.element; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -39,7 +38,7 @@ public class MUCLightChangeAffiliationsIQ extends IQ { public static final String ELEMENT = QUERY_ELEMENT; public static final String NAMESPACE = MultiUserChatLight.NAMESPACE + MultiUserChatLight.AFFILIATIONS; - private HashMap affiliations; + private Map affiliations; /** * MUCLight change affiliations IQ constructor. @@ -47,7 +46,8 @@ public class MUCLightChangeAffiliationsIQ extends IQ { * @param room TODO javadoc me please * @param affiliations TODO javadoc me please */ - public MUCLightChangeAffiliationsIQ(Jid room, HashMap affiliations) { + @SuppressWarnings("this-escape") + public MUCLightChangeAffiliationsIQ(Jid room, Map affiliations) { super(ELEMENT, NAMESPACE); this.setType(Type.set); this.setTo(room); @@ -59,7 +59,7 @@ public class MUCLightChangeAffiliationsIQ extends IQ { * * @return the affiliations */ - public HashMap getAffiliations() { + public Map getAffiliations() { return affiliations; } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightCreateIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightCreateIQ.java index d15d47db5..ea0db3b21 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightCreateIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightCreateIQ.java @@ -18,6 +18,7 @@ package org.jivesoftware.smackx.muclight.element; import java.util.HashMap; import java.util.List; +import java.util.Map; import org.jivesoftware.smack.packet.IQ; @@ -42,7 +43,7 @@ public class MUCLightCreateIQ extends IQ { public static final String NAMESPACE = MultiUserChatLight.NAMESPACE + MultiUserChatLight.CREATE; private MUCLightRoomConfiguration configuration; - private final HashMap occupants; + private final Map occupants; /** * MUCLight create IQ constructor. @@ -53,7 +54,8 @@ public class MUCLightCreateIQ extends IQ { * @param customConfigs TODO javadoc me please * @param occupants TODO javadoc me please */ - public MUCLightCreateIQ(EntityJid room, String roomName, String subject, HashMap customConfigs, + @SuppressWarnings("this-escape") + public MUCLightCreateIQ(EntityJid room, String roomName, String subject, Map customConfigs, List occupants) { super(ELEMENT, NAMESPACE); this.configuration = new MUCLightRoomConfiguration(roomName, subject, customConfigs); @@ -92,7 +94,7 @@ public class MUCLightCreateIQ extends IQ { * * @return the room occupants */ - public HashMap getOccupants() { + public Map getOccupants() { return occupants; } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightDestroyIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightDestroyIQ.java index 8198007cf..6606ae035 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightDestroyIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightDestroyIQ.java @@ -38,6 +38,7 @@ public class MUCLightDestroyIQ extends IQ { * * @param roomJid TODO javadoc me please */ + @SuppressWarnings("this-escape") public MUCLightDestroyIQ(Jid roomJid) { super(ELEMENT, NAMESPACE); this.setType(Type.set); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightElements.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightElements.java index 33861b29e..27dcbd2e7 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightElements.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightElements.java @@ -16,7 +16,6 @@ */ package org.jivesoftware.smackx.muclight.element; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -48,11 +47,11 @@ public abstract class MUCLightElements { public static final String NAMESPACE = MultiUserChatLight.NAMESPACE + MultiUserChatLight.AFFILIATIONS; public static final QName QNAME = new QName(NAMESPACE, ELEMENT); - private final HashMap affiliations; + private final Map affiliations; private final String prevVersion; private final String version; - public AffiliationsChangeExtension(HashMap affiliations, String prevVersion, + public AffiliationsChangeExtension(Map affiliations, String prevVersion, String version) { this.affiliations = affiliations; this.prevVersion = prevVersion; @@ -74,7 +73,7 @@ public abstract class MUCLightElements { * * @return the affiliations */ - public HashMap getAffiliations() { + public Map getAffiliations() { return affiliations; } @@ -135,7 +134,7 @@ public abstract class MUCLightElements { private final String version; private final String roomName; private final String subject; - private final HashMap customConfigs; + private final Map customConfigs; /** * Configurations change extension constructor. @@ -147,7 +146,7 @@ public abstract class MUCLightElements { * @param customConfigs TODO javadoc me please */ public ConfigurationsChangeExtension(String prevVersion, String version, String roomName, String subject, - HashMap customConfigs) { + Map customConfigs) { this.prevVersion = prevVersion; this.version = version; this.roomName = roomName; @@ -206,7 +205,7 @@ public abstract class MUCLightElements { * * @return the room custom configurations */ - public HashMap getCustomConfigs() { + public Map getCustomConfigs() { return customConfigs; } @@ -287,14 +286,14 @@ public abstract class MUCLightElements { */ public static class OccupantsElement implements Element { - private HashMap occupants; + private Map occupants; /** * Occupants element constructor. * * @param occupants TODO javadoc me please */ - public OccupantsElement(HashMap occupants) { + public OccupantsElement(Map occupants) { this.occupants = occupants; } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightGetAffiliationsIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightGetAffiliationsIQ.java index 67cd26481..70249914c 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightGetAffiliationsIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightGetAffiliationsIQ.java @@ -41,6 +41,7 @@ public class MUCLightGetAffiliationsIQ extends IQ { * @param roomJid TODO javadoc me please * @param version TODO javadoc me please */ + @SuppressWarnings("this-escape") public MUCLightGetAffiliationsIQ(Jid roomJid, String version) { super(ELEMENT, NAMESPACE); this.version = version; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightGetConfigsIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightGetConfigsIQ.java index 15ce3e1f9..aff4283ee 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightGetConfigsIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightGetConfigsIQ.java @@ -41,6 +41,7 @@ public class MUCLightGetConfigsIQ extends IQ { * @param roomJid TODO javadoc me please * @param version TODO javadoc me please */ + @SuppressWarnings("this-escape") public MUCLightGetConfigsIQ(Jid roomJid, String version) { super(ELEMENT, NAMESPACE); this.version = version; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightGetInfoIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightGetInfoIQ.java index 112ec1935..6e3d1daa0 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightGetInfoIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightGetInfoIQ.java @@ -41,6 +41,7 @@ public class MUCLightGetInfoIQ extends IQ { * @param roomJid TODO javadoc me please * @param version TODO javadoc me please */ + @SuppressWarnings("this-escape") public MUCLightGetInfoIQ(Jid roomJid, String version) { super(ELEMENT, NAMESPACE); this.version = version; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightInfoIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightInfoIQ.java index 42a2b9308..ef33d4b77 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightInfoIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightInfoIQ.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.muclight.element; -import java.util.HashMap; +import java.util.Map; import org.jivesoftware.smack.packet.IQ; @@ -41,7 +41,7 @@ public class MUCLightInfoIQ extends IQ { private final String version; private final MUCLightRoomConfiguration configuration; - private final HashMap occupants; + private final Map occupants; /** * MUCLight info response IQ constructor. @@ -51,7 +51,7 @@ public class MUCLightInfoIQ extends IQ { * @param occupants TODO javadoc me please */ public MUCLightInfoIQ(String version, MUCLightRoomConfiguration configuration, - HashMap occupants) { + Map occupants) { super(ELEMENT, NAMESPACE); this.version = version; this.configuration = configuration; @@ -90,7 +90,7 @@ public class MUCLightInfoIQ extends IQ { * * @return the occupants of the room */ - public HashMap getOccupants() { + public Map getOccupants() { return occupants; } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightSetConfigsIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightSetConfigsIQ.java index 0751b582f..3e332194a 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightSetConfigsIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/element/MUCLightSetConfigsIQ.java @@ -16,7 +16,6 @@ */ package org.jivesoftware.smackx.muclight.element; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -39,7 +38,7 @@ public class MUCLightSetConfigsIQ extends IQ { private String roomName; private String subject; - private HashMap customConfigs; + private Map customConfigs; /** * MUC Light set configuration IQ constructor. @@ -49,7 +48,8 @@ public class MUCLightSetConfigsIQ extends IQ { * @param subject TODO javadoc me please * @param customConfigs TODO javadoc me please */ - public MUCLightSetConfigsIQ(Jid roomJid, String roomName, String subject, HashMap customConfigs) { + @SuppressWarnings("this-escape") + public MUCLightSetConfigsIQ(Jid roomJid, String roomName, String subject, Map customConfigs) { super(ELEMENT, NAMESPACE); this.roomName = roomName; this.subject = subject; @@ -65,7 +65,7 @@ public class MUCLightSetConfigsIQ extends IQ { * @param roomName TODO javadoc me please * @param customConfigs TODO javadoc me please */ - public MUCLightSetConfigsIQ(Jid roomJid, String roomName, HashMap customConfigs) { + public MUCLightSetConfigsIQ(Jid roomJid, String roomName, Map customConfigs) { this(roomJid, roomName, null, customConfigs); } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/provider/MUCLightBlockingIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/provider/MUCLightBlockingIQProvider.java index b89dba5c8..1412b2900 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/provider/MUCLightBlockingIQProvider.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/provider/MUCLightBlockingIQProvider.java @@ -18,6 +18,7 @@ package org.jivesoftware.smackx.muclight.provider; import java.io.IOException; import java.util.HashMap; +import java.util.Map; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IqData; @@ -42,8 +43,8 @@ public class MUCLightBlockingIQProvider extends IqProvider { @Override public MUCLightBlockingIQ parse(XmlPullParser parser, int initialDepth, IqData iqData, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException { - HashMap rooms = null; - HashMap users = null; + Map rooms = null; + Map users = null; outerloop: while (true) { XmlPullParser.Event eventType = parser.next(); @@ -70,7 +71,7 @@ public class MUCLightBlockingIQProvider extends IqProvider { return mucLightBlockingIQ; } - private static HashMap parseBlocking(XmlPullParser parser, HashMap map) + private static Map parseBlocking(XmlPullParser parser, Map map) throws XmppStringprepException, XmlPullParserException, IOException { if (map == null) { map = new HashMap<>(); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/provider/MUCLightInfoIQProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/provider/MUCLightInfoIQProvider.java index 54d0a4218..50d30bca4 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/provider/MUCLightInfoIQProvider.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/provider/MUCLightInfoIQProvider.java @@ -18,6 +18,7 @@ package org.jivesoftware.smackx.muclight.provider; import java.io.IOException; import java.util.HashMap; +import java.util.Map; import org.jivesoftware.smack.packet.IqData; import org.jivesoftware.smack.packet.XmlEnvironment; @@ -45,8 +46,8 @@ public class MUCLightInfoIQProvider extends IqProvider { String version = null; String roomName = null; String subject = null; - HashMap customConfigs = null; - HashMap occupants = new HashMap<>(); + Map customConfigs = null; + Map occupants = new HashMap<>(); outerloop: while (true) { XmlPullParser.Event eventType = parser.next(); @@ -97,8 +98,8 @@ public class MUCLightInfoIQProvider extends IqProvider { return new MUCLightInfoIQ(version, new MUCLightRoomConfiguration(roomName, subject, customConfigs), occupants); } - private static HashMap iterateOccupants(XmlPullParser parser) throws XmlPullParserException, IOException { - HashMap occupants = new HashMap<>(); + private static Map iterateOccupants(XmlPullParser parser) throws XmlPullParserException, IOException { + Map occupants = new HashMap<>(); int depth = parser.getDepth(); outerloop: while (true) { diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/push_notifications/PushNotificationsManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/push_notifications/PushNotificationsManager.java index ff62d96eb..8615b4759 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/push_notifications/PushNotificationsManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/push_notifications/PushNotificationsManager.java @@ -16,7 +16,6 @@ */ package org.jivesoftware.smackx.push_notifications; -import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; @@ -122,7 +121,7 @@ public final class PushNotificationsManager extends Manager { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public boolean enable(Jid pushJid, String node, HashMap publishOptions) + public boolean enable(Jid pushJid, String node, Map publishOptions) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { EnablePushNotificationsIQ enablePushNotificationsIQ = new EnablePushNotificationsIQ(pushJid, node, publishOptions); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/push_notifications/element/DisablePushNotificationsIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/push_notifications/element/DisablePushNotificationsIQ.java index bedf8be02..08342811b 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/push_notifications/element/DisablePushNotificationsIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/push_notifications/element/DisablePushNotificationsIQ.java @@ -43,6 +43,7 @@ public class DisablePushNotificationsIQ extends IQ { private final Jid jid; private final String node; + @SuppressWarnings("this-escape") public DisablePushNotificationsIQ(Jid jid, String node) { super(ELEMENT, NAMESPACE); this.jid = jid; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/push_notifications/element/EnablePushNotificationsIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/push_notifications/element/EnablePushNotificationsIQ.java index 428b41b67..c6f8199b4 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/push_notifications/element/EnablePushNotificationsIQ.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/push_notifications/element/EnablePushNotificationsIQ.java @@ -16,7 +16,6 @@ */ package org.jivesoftware.smackx.push_notifications.element; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -51,9 +50,10 @@ public class EnablePushNotificationsIQ extends IQ { private final Jid jid; private final String node; - private final HashMap publishOptions; + private final Map publishOptions; - public EnablePushNotificationsIQ(Jid jid, String node, HashMap publishOptions) { + @SuppressWarnings("this-escape") + public EnablePushNotificationsIQ(Jid jid, String node, Map publishOptions) { super(ELEMENT, NAMESPACE); this.jid = jid; this.node = node; @@ -88,7 +88,7 @@ public class EnablePushNotificationsIQ extends IQ { * * @return the publish options */ - public HashMap getPublishOptions() { + public Map getPublishOptions() { return publishOptions; } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/reference/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/reference/package-info.java index 821577179..fc8f020be 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/reference/package-info.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/reference/package-info.java @@ -29,7 +29,7 @@ * *

        * 
      - * Message message = new Message("Alice is a realy nice person.");
      + * Message message = new Message("Alice is a really nice person.");
        * BareJid alice = JidCreate.bareFrom("alice@capulet.lit");
        * ReferenceManager.addMention(message, 0, 5, alice);
        * 
      diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/element/OriginIdElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/element/OriginIdElement.java
      index a50fd99e2..f0766de8b 100644
      --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/element/OriginIdElement.java
      +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/element/OriginIdElement.java
      @@ -38,28 +38,6 @@ public class OriginIdElement extends StableAndUniqueIdElement {
               super(id);
           }
       
      -    /**
      -     * Add an origin-id element to a message and set the stanzas id to the same id as in the origin-id element.
      -     *
      -     * @param message message.
      -     * @return the added origin-id element.
      -     * @deprecated use {@link #addTo(MessageBuilder)} instead.
      -     */
      -    @Deprecated
      -    // TODO: Remove in Smack 4.5.
      -    public static OriginIdElement addOriginId(Message message) {
      -        OriginIdElement originId = message.getExtension(OriginIdElement.class);
      -        if (originId != null) {
      -            return originId;
      -        }
      -
      -        originId = new OriginIdElement();
      -        message.addExtension(originId);
      -        // TODO: Find solution to have both the originIds stanzaId and a nice to look at incremental stanzaID.
      -        // message.setStanzaId(originId.getId());
      -        return originId;
      -    }
      -
           /**
            * Add an origin-id element to a message and set the stanzas id to the same id as in the origin-id element.
            *
      diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/spoiler/element/SpoilerElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/spoiler/element/SpoilerElement.java
      index 6d5c02fd6..b18a5af5c 100644
      --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/spoiler/element/SpoilerElement.java
      +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/spoiler/element/SpoilerElement.java
      @@ -114,6 +114,7 @@ public class SpoilerElement implements ExtensionElement {
            * @param message message
            * @return map of spoilers
            */
      +    @SuppressWarnings("MixedMutabilityReturnType")
           public static Map getSpoilers(Message message) {
               if (!containsSpoiler(message)) {
                   return Collections.emptyMap();
      diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/stanza_content_encryption/element/TimestampAffixElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/stanza_content_encryption/element/TimestampAffixElement.java
      index 0c48f7b7a..eee726085 100644
      --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/stanza_content_encryption/element/TimestampAffixElement.java
      +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/stanza_content_encryption/element/TimestampAffixElement.java
      @@ -56,6 +56,7 @@ public class TimestampAffixElement implements NamedElement, AffixElement {
               return EqualsUtil.equals(this, obj, (e, o) -> e.append(getTimestamp(), o.getTimestamp()));
           }
       
      +    @SuppressWarnings("JavaUtilDate")
           @Override
           public int hashCode() {
               return timestamp.hashCode();
      diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/eme/ExplicitMessageEncryptionElementTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/eme/ExplicitMessageEncryptionElementTest.java
      index dc14db131..b9364b01f 100644
      --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/eme/ExplicitMessageEncryptionElementTest.java
      +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/eme/ExplicitMessageEncryptionElementTest.java
      @@ -39,7 +39,7 @@ public class ExplicitMessageEncryptionElementTest extends SmackTestSuite {
           public void addToMessageTest() {
               Message message = StanzaBuilder.buildMessage().build();
       
      -        // Check inital state (no elements)
      +        // Check initial state (no elements)
               assertNull(ExplicitMessageEncryptionElement.from(message));
               assertFalse(ExplicitMessageEncryptionElement.hasProtocol(message,
                       ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.omemoVAxolotl));
      @@ -75,7 +75,7 @@ public class ExplicitMessageEncryptionElementTest extends SmackTestSuite {
               assertTrue(ExplicitMessageEncryptionElement.hasProtocol(message,
                       ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.omemoVAxolotl));
       
      -        // Check, if adding additional OMEMO wont add another element
      +        // Check, if adding additional OMEMO won't add another element
               ExplicitMessageEncryptionElement.set(messageBuilder,
                       ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.omemoVAxolotl);
       
      diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java
      index b5c974dce..df14ea279 100644
      --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java
      +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/FiltersTest.java
      @@ -1,6 +1,6 @@
       /**
        *
      - * Copyright 2016 Fernando Ramirez, 2018-2020 Florian Schmaus
      + * Copyright 2016 Fernando Ramirez, 2018-2024 Florian Schmaus
        *
        * Licensed under the Apache License, Version 2.0 (the "License");
        * you may not use this file except in compliance with the License.
      @@ -46,6 +46,7 @@ public class FiltersTest extends MamTest {
               return xml;
           }
       
      +    @SuppressWarnings("JavaUtilDate")
           @Test
           public void checkStartDateFilter() throws Exception {
               Date date = new Date();
      @@ -61,6 +62,7 @@ public class FiltersTest extends MamTest {
               assertEquals(getMamXMemberWith(fields, values), dataForm.toXML().toString());
           }
       
      +    @SuppressWarnings("JavaUtilDate")
           @Test
           public void checkEndDateFilter() throws Exception {
               Date date = new Date();
      diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightAffiliationsChangeExtensionTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightAffiliationsChangeExtensionTest.java
      index a07577748..ab9e53750 100644
      --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightAffiliationsChangeExtensionTest.java
      +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightAffiliationsChangeExtensionTest.java
      @@ -18,7 +18,7 @@ package org.jivesoftware.smackx.muclight;
       
       import static org.junit.jupiter.api.Assertions.assertEquals;
       
      -import java.util.HashMap;
      +import java.util.Map;
       
       import org.jivesoftware.smack.packet.Message;
       import org.jivesoftware.smack.util.PacketParserUtils;
      @@ -55,7 +55,7 @@ public class MUCLightAffiliationsChangeExtensionTest {
               AffiliationsChangeExtension affiliationsChangeExtension = AffiliationsChangeExtension
                       .from(changeAffiliationsMessage);
       
      -        HashMap affiliations = affiliationsChangeExtension.getAffiliations();
      +        Map affiliations = affiliationsChangeExtension.getAffiliations();
               assertEquals(affiliations.size(), 3);
               assertEquals(affiliations.get(JidCreate.from("sarasa2@shakespeare.lit")), MUCLightAffiliation.owner);
               assertEquals(affiliations.get(JidCreate.from("sarasa1@shakespeare.lit")), MUCLightAffiliation.member);
      @@ -68,7 +68,7 @@ public class MUCLightAffiliationsChangeExtensionTest {
               AffiliationsChangeExtension affiliationsChangeExtension = AffiliationsChangeExtension
                       .from(changeAffiliationsMessage);
       
      -        HashMap affiliations = affiliationsChangeExtension.getAffiliations();
      +        Map affiliations = affiliationsChangeExtension.getAffiliations();
               assertEquals(affiliations.size(), 2);
               assertEquals(affiliations.get(JidCreate.from("sarasa1@shakespeare.lit")), MUCLightAffiliation.member);
               assertEquals(affiliations.get(JidCreate.from("sarasa3@shakespeare.lit")), MUCLightAffiliation.none);
      @@ -83,7 +83,7 @@ public class MUCLightAffiliationsChangeExtensionTest {
               AffiliationsChangeExtension affiliationsChangeExtension = AffiliationsChangeExtension
                       .from(changeAffiliationsMessage);
       
      -        HashMap affiliations = affiliationsChangeExtension.getAffiliations();
      +        Map affiliations = affiliationsChangeExtension.getAffiliations();
               assertEquals(affiliations.size(), 2);
               assertEquals(affiliations.get(JidCreate.from("sarasa2@shakespeare.lit")), MUCLightAffiliation.owner);
               assertEquals(affiliations.get(JidCreate.from("sarasa1@shakespeare.lit")), MUCLightAffiliation.member);
      diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightChangeAffiliationsIQTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightChangeAffiliationsIQTest.java
      index efbb22da7..ca0e1e4cb 100644
      --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightChangeAffiliationsIQTest.java
      +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightChangeAffiliationsIQTest.java
      @@ -19,6 +19,7 @@ package org.jivesoftware.smackx.muclight;
       import static org.junit.jupiter.api.Assertions.assertEquals;
       
       import java.util.HashMap;
      +import java.util.Map;
       
       import org.jivesoftware.smack.packet.IQ;
       
      @@ -32,7 +33,7 @@ public class MUCLightChangeAffiliationsIQTest {
       
           @Test
           public void checkChangeAffiliationsMUCLightStanza() throws Exception {
      -        HashMap affiliations = new HashMap<>();
      +        Map affiliations = new HashMap<>();
               affiliations.put(JidCreate.from("sarasa2@shakespeare.lit"), MUCLightAffiliation.owner);
               affiliations.put(JidCreate.from("sarasa1@shakespeare.lit"), MUCLightAffiliation.member);
               affiliations.put(JidCreate.from("sarasa3@shakespeare.lit"), MUCLightAffiliation.none);
      @@ -44,7 +45,7 @@ public class MUCLightChangeAffiliationsIQTest {
               assertEquals(mucLightChangeAffiliationsIQ.getTo(), "coven@muclight.shakespeare.lit");
               assertEquals(mucLightChangeAffiliationsIQ.getType(), IQ.Type.set);
       
      -        HashMap iqAffiliations = mucLightChangeAffiliationsIQ.getAffiliations();
      +        Map iqAffiliations = mucLightChangeAffiliationsIQ.getAffiliations();
               assertEquals(iqAffiliations.get(JidCreate.from("sarasa1@shakespeare.lit")), MUCLightAffiliation.member);
               assertEquals(iqAffiliations.get(JidCreate.from("sarasa2@shakespeare.lit")), MUCLightAffiliation.owner);
               assertEquals(iqAffiliations.get(JidCreate.from("sarasa3@shakespeare.lit")), MUCLightAffiliation.none);
      diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightCreateIQTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightCreateIQTest.java
      index 216a2a494..4a92da24e 100644
      --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightCreateIQTest.java
      +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightCreateIQTest.java
      @@ -19,8 +19,8 @@ package org.jivesoftware.smackx.muclight;
       import static org.junit.jupiter.api.Assertions.assertEquals;
       
       import java.util.ArrayList;
      -import java.util.HashMap;
       import java.util.List;
      +import java.util.Map;
       
       import org.jivesoftware.smackx.muclight.element.MUCLightCreateIQ;
       
      @@ -43,7 +43,7 @@ public class MUCLightCreateIQTest {
       
               assertEquals(mucLightCreateIQ.getConfiguration().getRoomName(), "test");
       
      -        HashMap iqOccupants = mucLightCreateIQ.getOccupants();
      +        Map iqOccupants = mucLightCreateIQ.getOccupants();
               assertEquals(iqOccupants.get(JidCreate.from("charlie@test.com")), MUCLightAffiliation.member);
               assertEquals(iqOccupants.get(JidCreate.from("pep@test.com")), MUCLightAffiliation.member);
           }
      diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightGetAffiliationsTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightGetAffiliationsTest.java
      index 782171ae1..5504468ef 100644
      --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightGetAffiliationsTest.java
      +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightGetAffiliationsTest.java
      @@ -18,7 +18,7 @@ package org.jivesoftware.smackx.muclight;
       
       import static org.junit.jupiter.api.Assertions.assertEquals;
       
      -import java.util.HashMap;
      +import java.util.Map;
       
       import org.jivesoftware.smack.packet.IQ;
       import org.jivesoftware.smack.packet.StreamOpen;
      @@ -57,7 +57,7 @@ public class MUCLightGetAffiliationsTest {
       
               assertEquals("123456", mucLightAffiliationsIQ.getVersion());
       
      -        HashMap affiliations = mucLightAffiliationsIQ.getAffiliations();
      +        Map affiliations = mucLightAffiliationsIQ.getAffiliations();
               assertEquals(3, affiliations.size());
               assertEquals(MUCLightAffiliation.owner, affiliations.get(JidCreate.from("user1@shakespeare.lit")));
               assertEquals(MUCLightAffiliation.member, affiliations.get(JidCreate.from("user2@shakespeare.lit")));
      diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightGetConfigsTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightGetConfigsTest.java
      index 8c295ab0e..02826d8d1 100644
      --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightGetConfigsTest.java
      +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/muclight/MUCLightGetConfigsTest.java
      @@ -19,7 +19,7 @@ package org.jivesoftware.smackx.muclight;
       import static org.junit.jupiter.api.Assertions.assertEquals;
       import static org.junit.jupiter.api.Assertions.assertNull;
       
      -import java.util.HashMap;
      +import java.util.Map;
       
       import org.jivesoftware.smack.packet.IQ;
       import org.jivesoftware.smack.packet.StreamOpen;
      @@ -74,7 +74,7 @@ public class MUCLightGetConfigsTest {
               assertEquals("A Dark Cave", mucLightConfigurationIQ.getConfiguration().getRoomName());
               assertNull(mucLightConfigurationIQ.getConfiguration().getSubject());
       
      -        HashMap customConfigs = mucLightConfigurationIQ.getConfiguration().getCustomConfigs();
      +        Map customConfigs = mucLightConfigurationIQ.getConfiguration().getCustomConfigs();
               assertEquals("blue", customConfigs.get("color"));
               assertEquals("20", customConfigs.get("size"));
           }
      diff --git a/smack-extensions/build.gradle b/smack-extensions/build.gradle
      index 2489c9f87..0b62da32c 100644
      --- a/smack-extensions/build.gradle
      +++ b/smack-extensions/build.gradle
      @@ -1,3 +1,8 @@
      +plugins {
      +	id 'org.igniterealtime.smack.java-common-conventions'
      +	id 'org.igniterealtime.smack.android-conventions'
      +}
      +
       description = """\
       Smack extensions.
       Classes and methods that implement support for the various XMPP XEPs
      diff --git a/smack-extensions/src/integration-test/java/org/jivesoftware/smackx/FileTransferTest.java b/smack-extensions/src/integration-test/java/org/jivesoftware/smackx/FileTransferTest.java
      index b3ecc362a..fc9c6d933 100644
      --- a/smack-extensions/src/integration-test/java/org/jivesoftware/smackx/FileTransferTest.java
      +++ b/smack-extensions/src/integration-test/java/org/jivesoftware/smackx/FileTransferTest.java
      @@ -109,7 +109,7 @@ public class FileTransferTest extends SmackTestCase {
                   fail();
               }
               byte [] array = queue.take();
      -        assertEquals("Recieved file not equal to sent file.", testTransfer, array);
      +        assertEquals("Received file not equal to sent file.", testTransfer, array);
           }
       
       
      diff --git a/smack-extensions/src/integration-test/java/org/jivesoftware/smackx/OfflineMessageManagerTest.java b/smack-extensions/src/integration-test/java/org/jivesoftware/smackx/OfflineMessageManagerTest.java
      index 13d4bd23c..5efb0799a 100644
      --- a/smack-extensions/src/integration-test/java/org/jivesoftware/smackx/OfflineMessageManagerTest.java
      +++ b/smack-extensions/src/integration-test/java/org/jivesoftware/smackx/OfflineMessageManagerTest.java
      @@ -50,7 +50,7 @@ public class OfflineMessageManagerTest extends SmackTestCase {
           /**
            * While user2 is connected but unavailable, user1 sends 2 messages to user1. User2 then
            * performs some "Flexible Offline Message Retrieval" checking the number of offline messages,
      -     * retriving the headers, then the real messages of the headers and finally removing the
      +     * retrieving the headers, then the real messages of the headers and finally removing the
            * loaded messages.
            */
           public void testReadAndDelete() {
      diff --git a/smack-extensions/src/integration-test/java/org/jivesoftware/smackx/ServiceDiscoveryManagerTest.java b/smack-extensions/src/integration-test/java/org/jivesoftware/smackx/ServiceDiscoveryManagerTest.java
      index 5d56d85ba..71ba8d7d2 100644
      --- a/smack-extensions/src/integration-test/java/org/jivesoftware/smackx/ServiceDiscoveryManagerTest.java
      +++ b/smack-extensions/src/integration-test/java/org/jivesoftware/smackx/ServiceDiscoveryManagerTest.java
      @@ -90,7 +90,7 @@ public class ServiceDiscoveryManagerTest extends SmackTestCase {
            */
           public void testXHTMLFeature() {
               // Check for local XHTML service support
      -        // By default the XHTML service support is enabled in all the connections
      +        // By default,the XHTML service support is enabled in all the connections
               assertTrue(XHTMLManager.isServiceEnabled(getConnection(0)));
               assertTrue(XHTMLManager.isServiceEnabled(getConnection(1)));
               // Check for XHTML support in connection1 from connection2
      diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/address/packet/MultipleAddresses.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/address/packet/MultipleAddresses.java
      index fba5979ce..6f9e63e81 100644
      --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/address/packet/MultipleAddresses.java
      +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/address/packet/MultipleAddresses.java
      @@ -93,7 +93,7 @@ public class MultipleAddresses implements ExtensionElement {
       
           /**
            * Returns the list of addresses that matches the specified type. Examples of address
      -     * type are: TO, CC, BCC, etc..
      +     * type are: TO, CC, BCC, etc.
            *
            * @param type Examples of address type are: TO, CC, BCC, etc.
            * @return the list of addresses that matches the specified type.
      diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/blocking/BlockingCommandManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/blocking/BlockingCommandManager.java
      index 6769c9f5b..330cfbc8c 100644
      --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/blocking/BlockingCommandManager.java
      +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/blocking/BlockingCommandManager.java
      @@ -44,13 +44,13 @@ import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
       import org.jxmpp.jid.Jid;
       
       /**
      - * Block communications with contancts and other entities using XEP-0191.
      + * Block communications with contacts and other entities using XEP-0191.
        * Allows to
        * 
        *
      • Check push notifications support
      • *
      • Get blocking list
      • *
      • Block contact
      • - *
      • Unblock conact
      • + *
      • Unblock contact
      • *
      • Unblock all
      • *
      * diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/blocking/element/BlockContactsIQ.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/blocking/element/BlockContactsIQ.java index 39cb9525b..79bd2068a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/blocking/element/BlockContactsIQ.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/blocking/element/BlockContactsIQ.java @@ -51,6 +51,7 @@ public class BlockContactsIQ extends IQ { * * @param jids TODO javadoc me please */ + @SuppressWarnings("this-escape") public BlockContactsIQ(List jids) { super(ELEMENT, NAMESPACE); this.setType(Type.set); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/blocking/element/UnblockContactsIQ.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/blocking/element/UnblockContactsIQ.java index e0a28f722..efba53e3a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/blocking/element/UnblockContactsIQ.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/blocking/element/UnblockContactsIQ.java @@ -52,6 +52,7 @@ public class UnblockContactsIQ extends IQ { * * @param jids TODO javadoc me please */ + @SuppressWarnings("this-escape") public UnblockContactsIQ(List jids) { super(ELEMENT, NAMESPACE); this.setType(Type.set); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/element/BoBIQ.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/element/BoBIQ.java index 085df5fd5..2790e3860 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/element/BoBIQ.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/element/BoBIQ.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016 Fernando Ramirez, 2020 Florian Schmaus + * Copyright 2016 Fernando Ramirez, 2020-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -65,18 +65,6 @@ public class BoBIQ extends IQ { this(cid, null); } - /** - * Get the BoB hash. - * - * @return the BoB hash - * @deprecated use {@link #getContentId()} instead. - */ - // TODO: Remove in Smack 4.5. - @Deprecated - public ContentId getBoBHash() { - return cid; - } - /** * Get the BoB hash. * diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bookmarks/BookmarkManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bookmarks/BookmarkManager.java index fe8323953..9d00ec626 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bookmarks/BookmarkManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bookmarks/BookmarkManager.java @@ -103,7 +103,7 @@ public final class BookmarkManager { * * @param name the name of the conference * @param jid the jid of the conference - * @param isAutoJoin whether or not to join this conference automatically on login + * @param isAutoJoin whether to join this conference automatically on login * @param nickname the nickname to use for the user when joining the conference * @param password the password to use for the user when joining the conference * @throws XMPPErrorException thrown when there is an issue retrieving the current bookmarks from @@ -166,7 +166,7 @@ public final class BookmarkManager { * Returns an unmodifiable collection of all bookmarked urls. * * @return returns an unmodifiable collection of all bookmarked urls. - * @throws XMPPErrorException thrown when there is a problem retriving bookmarks from the server. + * @throws XMPPErrorException thrown when there is a problem retrieving bookmarks from the server. * @throws NoResponseException if there was no response from the server. * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. @@ -181,8 +181,8 @@ public final class BookmarkManager { * * @param URL the url of the bookmark * @param name the name of the bookmark - * @param isRSS whether or not the url is an rss feed - * @throws XMPPErrorException thrown when there is an error retriving or saving bookmarks from or to + * @param isRSS whether the url is an RSS feed + * @throws XMPPErrorException thrown when there is an error retrieving or saving bookmarks from or to * the server * @throws NoResponseException if there was no response from the server. * @throws NotConnectedException if the XMPP connection is not connected. @@ -210,7 +210,7 @@ public final class BookmarkManager { * Removes a url from the bookmarks. * * @param bookmarkURL the url of the bookmark to remove - * @throws XMPPErrorException thrown if there is an error retriving or saving bookmarks from or to + * @throws XMPPErrorException thrown if there is an error retrieving or saving bookmarks from or to * the server. * @throws NoResponseException if there was no response from the server. * @throws NotConnectedException if the XMPP connection is not connected. diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java index 9061a5606..e527d416a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java @@ -16,8 +16,8 @@ */ package org.jivesoftware.smackx.bytestreams.ibb; +import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.WeakHashMap; @@ -135,7 +135,7 @@ public final class InBandBytestreamManager extends Manager implements Bytestream * list of listeners that respond to all In-Band Bytestream requests if there are no user * specific listeners for that request */ - private final List allRequestListeners = Collections.synchronizedList(new LinkedList()); + private final List allRequestListeners = Collections.synchronizedList(new ArrayList()); /* listener that handles all incoming In-Band Bytestream requests */ private final InitiationListener initiationListener; @@ -162,7 +162,7 @@ public final class InBandBytestreamManager extends Manager implements Bytestream * list containing session IDs of In-Band Bytestream open packets that should be ignored by the * InitiationListener */ - private final List ignoredBytestreamRequests = Collections.synchronizedList(new LinkedList()); + private final List ignoredBytestreamRequests = Collections.synchronizedList(new ArrayList()); /** * Returns the InBandBytestreamManager to handle In-Band Bytestreams for a given @@ -511,7 +511,7 @@ public final class InBandBytestreamManager extends Manager implements Bytestream } /** - * Returns the list of session IDs that should be ignored by the InitialtionListener + * Returns the list of session IDs that should be ignored by the InitiationListener * * @return list of session IDs */ diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Close.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Close.java index cdd002590..9147cdd72 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Close.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Close.java @@ -36,6 +36,7 @@ public class Close extends IQ { * * @param sessionID unique session ID identifying this In-Band Bytestream */ + @SuppressWarnings("this-escape") public Close(String sessionID) { super(ELEMENT, NAMESPACE); if (sessionID == null || "".equals(sessionID)) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Data.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Data.java index 54bbaeb80..77ceb9450 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Data.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Data.java @@ -34,6 +34,7 @@ public class Data extends IQ { * * @param data data stanza extension containing the encoded data */ + @SuppressWarnings("this-escape") public Data(DataPacketExtension data) { super(DataPacketExtension.ELEMENT, DataPacketExtension.NAMESPACE); if (data == null) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Open.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Open.java index 768cf3f20..a2e54bdaf 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Open.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/packet/Open.java @@ -54,6 +54,7 @@ public class Open extends IQ { * @param blockSize block size in which the data will be fragmented * @param stanza stanza type used to encapsulate the data */ + @SuppressWarnings("this-escape") public Open(String sessionID, int blockSize, StanzaType stanza) { super(ELEMENT, NAMESPACE); if (sessionID == null || "".equals(sessionID)) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java index 673f7a989..fac036a1d 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java @@ -22,7 +22,6 @@ import java.net.Socket; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -128,7 +127,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream * list of listeners that respond to all bytestream requests if there are not user specific * listeners for that request */ - private final List allRequestListeners = Collections.synchronizedList(new LinkedList()); + private final List allRequestListeners = Collections.synchronizedList(new ArrayList()); /* listener that handles all incoming bytestream requests */ private final InitiationListener initiationListener; @@ -139,7 +138,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream /* timeout for connecting to the SOCKS5 proxy selected by the target */ private int proxyConnectionTimeout = 10000; - /* blacklist of errornous SOCKS5 proxies */ + /* blacklist of erroneous SOCKS5 proxies */ private final Set proxyBlacklist = Collections.synchronizedSet(new HashSet()); /* remember the last proxy that worked to prioritize it */ @@ -154,7 +153,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream * list containing session IDs of SOCKS5 Bytestream initialization packets that should be * ignored by the InitiationListener */ - private final List ignoredBytestreamRequests = Collections.synchronizedList(new LinkedList()); + private final List ignoredBytestreamRequests = Collections.synchronizedList(new ArrayList()); /** * Returns the Socks5BytestreamManager to handle SOCKS5 Bytestreams for a given @@ -390,7 +389,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream } /** - * Set whether or not the bytestream manager will annouce the local stream host(s), i.e. the local SOCKS5 proxy. + * Set whether the bytestream manager will announce the local stream host(s), i.e. the local SOCKS5 proxy. * * @param announceLocalStreamHost TODO javadoc me please * @see #isAnnouncingLocalStreamHostEnabled() @@ -580,7 +579,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream proxyInfo = serviceDiscoveryManager.discoverInfo(item.getEntityID()); } catch (NoResponseException | XMPPErrorException e) { - // blacklist errornous server + // blacklist erroneous server proxyBlacklist.add(item.getEntityID()); continue; } @@ -627,7 +626,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream streamHosts.addAll(response.getStreamHosts()); } catch (Exception e) { - // blacklist errornous proxies + // blacklist erroneous proxies this.proxyBlacklist.add(proxy); } } @@ -796,7 +795,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream } /** - * Returns the list of session IDs that should be ignored by the InitialtionListener + * Returns the list of session IDs that should be ignored by the InitiationListener * * @return list of session IDs */ diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java index 7f80e8260..e4444c6ac 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java @@ -138,7 +138,7 @@ public class Socks5Client { byte[] connectionRequest; byte[] connectionResponse; /* - * use DataInputStream/DataOutpuStream to assure read and write is completed in a single + * use DataInputStream/DataOutputStream to assure read and write is completed in a single * statement */ DataInputStream in = new DataInputStream(socket.getInputStream()); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java index 88e7abfe2..117333095 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java @@ -26,12 +26,12 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.LinkedHashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -107,7 +107,7 @@ public class Socks5Proxy { private final Map connectionMap = new ConcurrentHashMap<>(); /* list of digests connections should be stored */ - private final List allowedConnections = Collections.synchronizedList(new LinkedList()); + private final List allowedConnections = Collections.synchronizedList(new ArrayList()); private final Set localAddresses = new LinkedHashSet<>(4); @@ -149,6 +149,7 @@ public class Socks5Proxy { * * @param serverSocket the server socket to use */ + @SuppressWarnings("this-escape") protected Socks5Proxy(ServerSocket serverSocket) { this.serverProcess = new Socks5ServerProcess(); this.serverSocket = serverSocket; @@ -345,7 +346,7 @@ public class Socks5Proxy { */ public List getLocalAddresses() { synchronized (localAddresses) { - return new LinkedList<>(localAddresses); + return new ArrayList<>(localAddresses); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java index f050db26e..bc049a034 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java @@ -68,6 +68,7 @@ public class Bytestream extends IQ { * @param SID The session ID related to the negotiation. * @see #setSessionID(String) */ + @SuppressWarnings("this-escape") public Bytestream(final String SID) { this(); setSessionID(SID); @@ -298,7 +299,7 @@ public class Bytestream extends IQ { * @param port port of the stream host. */ public StreamHost(final Jid jid, final String address, int port) { - this(jid, InternetAddress.from(address), port); + this(jid, InternetAddress.fromIgnoringZoneId(address), port); } public StreamHost(Jid jid, InetAddress address, int port) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java index 8bd738fa2..aaa87e2b0 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2009 Jonas Ådahl, 2011-2022 Florian Schmaus + * Copyright © 2009 Jonas Ådahl, 2011-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,11 +19,12 @@ package org.jivesoftware.smackx.caps; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -90,7 +91,7 @@ import org.jxmpp.util.cache.LruCache; * but XEP-0030 then XEP-0030 mechanisms are transparently used. *

      *

      - * The caches used by Smack for Entity Capabilities is non-persisent per default. However, it is is also possible to set + * The caches used by Smack for Entity Capabilities is non-persistent per default. However, it is is also possible to set * a persistent Entity Capabilities cache, which is recommended. *

      *

      Examples

      @@ -225,7 +226,7 @@ public final class EntityCapsManager extends Manager { /** * Get the Node version (node#ver) of a JID. Returns a String or null if - * EntiyCapsManager does not have any information. + * EntityCapsManager does not have any information. * * @param jid TODO javadoc me please * the user (Full JID) @@ -554,7 +555,7 @@ public final class EntityCapsManager extends Manager { if (connection != null) JID_TO_NODEVER_CACHE.put(connection.getUser(), new NodeVerHash(entityNode, currentCapsVersion)); - final List identities = new LinkedList<>(ServiceDiscoveryManager.getInstanceFor(connection).getIdentities()); + final List identities = new ArrayList<>(ServiceDiscoveryManager.getInstanceFor(connection).getIdentities()); sdm.setNodeInformationProvider(localNodeVer, new AbstractNodeInformationProvider() { List features = sdm.getFeatures(); List packetExtensions = sdm.getExtendedInfo(); @@ -701,16 +702,30 @@ public final class EntityCapsManager extends Manager { } List extendedInfos = discoverInfo.getExtensions(DataForm.class); - for (DataForm extendedInfo : extendedInfos) { - if (!extendedInfo.hasHiddenFormTypeField()) { + final Iterator iter = extendedInfos.iterator(); + while (iter.hasNext()) { + if (!iter.next().hasHiddenFormTypeField()) { // Only use the data form for calculation is it has a hidden FORM_TYPE field. // See XEP-0115 5.4 step 3.f - continue; + iter.remove(); } + } - // 6. If the service discovery information response includes - // XEP-0128 data forms, sort the forms by the FORM_TYPE (i.e., - // by the XML character data of the element). + // 6. If the service discovery information response includes + // XEP-0128 data forms, sort the forms by the FORM_TYPE (i.e., + // by the XML character data of the element). + Collections.sort(extendedInfos, new Comparator() { + @Override + public int compare(DataForm dataFormLeft, DataForm dataFormRight) { + final String formTypeLeft = dataFormLeft.getFormType(); + assert formTypeLeft != null; // ensured by the previous step. + final String formTypeRight = dataFormRight.getFormType(); + assert formTypeRight != null; // ensured by the previous step. + return formTypeLeft.compareTo(formTypeRight); + } + }); + + for (DataForm extendedInfo : extendedInfos) { SortedSet fs = new TreeSet<>(new Comparator() { @Override public int compare(FormField f1, FormField f2) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandHandler.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandHandler.java index 181fa7c3d..5eb6d6dda 100755 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandHandler.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandHandler.java @@ -57,6 +57,7 @@ public abstract class AdHocCommandHandler extends AbstractAdHocCommand { */ private int currentStage; + @SuppressWarnings("this-escape") public AdHocCommandHandler(String node, String name, String sessionId) { super(node, name); setSessionId(sessionId); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java index 49bf9c0cd..568b98a14 100755 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java @@ -85,7 +85,7 @@ public final class AdHocCommandManager extends Manager { */ private static final Map instances = new WeakHashMap<>(); - /** + /* * Register the listener for all the connection creations. When a new * connection is created a new AdHocCommandManager is also created and * related to that connection. @@ -463,7 +463,7 @@ public final class AdHocCommandManager extends Manager { private AdHocCommandData processAdHocCommandOfExistingSession(AdHocCommandData request, AdHocCommandHandler command, AdHocCommandDataBuilder responseBuilder) { // Check if the Session data has expired (default is 10 minutes) long creationStamp = command.getCreationDate(); - if (System.currentTimeMillis() - creationStamp > sessionTimeoutSecs * 1000) { + if (System.currentTimeMillis() - creationStamp > sessionTimeoutSecs * 1000L) { // Remove the expired session executingCommands.remove(command.getSessionId()); @@ -553,7 +553,7 @@ public final class AdHocCommandManager extends Manager { // after the time out, then once the user requests to // continue the execution he will received an // invalid session error and not a time out error. - if (currentTime - creationStamp > getSessionRemovalTimeoutSecs() * 1000) { + if (currentTime - creationStamp > getSessionRemovalTimeoutSecs() * 1000L) { // Remove the expired session it.remove(); } @@ -654,7 +654,7 @@ public final class AdHocCommandManager extends Manager { public AdHocCommandHandler getCommandInstance() throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { String sessionId; - // TODO: The code below contains a race condition. Use CopncurrentHashMap.computeIfAbsent() to remove the + // TODO: The code below contains a race condition. Use ConcurrentHashMap.computeIfAbsent() to remove the // race condition once Smack's minimum Android API level 24 or higher. int attempt = 0; do { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/delay/filter/DelayedStanzaFilter.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/delay/filter/DelayedStanzaFilter.java index 054ed42cf..90c30f563 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/delay/filter/DelayedStanzaFilter.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/delay/filter/DelayedStanzaFilter.java @@ -23,14 +23,14 @@ import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smackx.delay.DelayInformationManager; /** - * Filters stanza with delay information, ie. stanzas that got delayed for some reason + * Filters stanza with delay information, i.e. stanzas that got delayed for some reason */ public final class DelayedStanzaFilter implements StanzaFilter { public static final StanzaFilter INSTANCE = new DelayedStanzaFilter(); /** - * Filters stanzas that got not delayed, ie. have no delayed information + * Filters stanzas that got not delayed, i.e. have no delayed information */ public static final StanzaFilter NOT_DELAYED_STANZA = new NotFilter(INSTANCE); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java index 6e43fbc9a..e50f6d956 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2018-2022 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2018-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -294,7 +293,7 @@ public final class ServiceDiscoveryManager extends Manager { /** * Returns all identities of this client as unmodifiable Collection. * - * @return all identies as set + * @return all identities as a set */ public Set getIdentities() { Set res = new HashSet<>(identities); @@ -442,27 +441,6 @@ public final class ServiceDiscoveryManager extends Manager { return features.contains(feature); } - /** - * Registers extended discovery information of this XMPP entity. When this - * client is queried for its information this data form will be returned as - * specified by XEP-0128. - *

      - * - * Since no stanza is actually sent to the server it is safe to perform this - * operation before logging to the server. In fact, you may want to - * configure the extended info before logging to the server so that the - * information is already available if it is required upon login. - * - * @param info the data form that contains the extend service discovery - * information. - * @deprecated use {@link #addExtendedInfo(DataForm)} instead. - */ - // TODO: Remove in Smack 4.5 - @Deprecated - public synchronized void setExtendedInfo(DataForm info) { - addExtendedInfo(info); - } - /** * Registers extended discovery information of this XMPP entity. When this * client is queried for its information this data form will be returned as @@ -518,19 +496,6 @@ public final class ServiceDiscoveryManager extends Manager { return CollectionUtil.newListWith(extendedInfos); } - /** - * Returns the data form as List of PacketExtensions, or null if no data form is set. - * This representation is needed by some classes (e.g. EntityCapsManager, NodeInformationProvider) - * - * @return the data form as List of PacketExtensions - * @deprecated use {@link #getExtendedInfo()} instead. - */ - // TODO: Remove in Smack 4.5 - @Deprecated - public List getExtendedInfoAsList() { - return getExtendedInfo(); - } - /** * Removes the data form containing extended service discovery information * from the information returned by this XMPP entity.

      @@ -806,7 +771,7 @@ public final class ServiceDiscoveryManager extends Manager { return serviceDiscoInfo; } } - serviceDiscoInfo = new LinkedList<>(); + serviceDiscoInfo = new ArrayList<>(); // Send the disco packet to the server itself DiscoverInfo info; try { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java index abe18f630..9df5c64ac 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java @@ -17,10 +17,8 @@ package org.jivesoftware.smackx.disco.packet; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -83,17 +81,6 @@ public class DiscoverInfo extends IQ implements DiscoverInfoView { } } - /** - * Deprecated. - * - * @deprecated use {@link DiscoverInfoBuilder} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public DiscoverInfo() { - super(ELEMENT, NAMESPACE); - } - /** * Copy constructor. * @@ -114,85 +101,11 @@ public class DiscoverInfo extends IQ implements DiscoverInfoView { identitiesSet.addAll(d.identitiesSet); } - /** - * Adds a new feature to the discovered information. - * - * @param feature the discovered feature - * @return true if the feature did not already exist. - * @deprecated use {@link DiscoverInfoBuilder#addFeature(String)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public boolean addFeature(String feature) { - return addFeature(new Feature(feature)); - } - - /** - * Adds a collection of features to the packet. Does noting if featuresToAdd is null. - * - * @param featuresToAdd TODO javadoc me please - * @deprecated use {@link DiscoverInfoBuilder#addFeatures(Collection)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public void addFeatures(Collection featuresToAdd) { - if (featuresToAdd == null) return; - for (String feature : featuresToAdd) { - addFeature(feature); - } - } - - /** - * Deprecated. - * - * @param feature the future. - * @return true if the feature is new. - * @deprecated use {@link DiscoverInfoBuilder#addFeature(DiscoverInfo.Feature)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public boolean addFeature(Feature feature) { - features.add(feature); - boolean featureIsNew = featuresSet.add(feature); - if (!featureIsNew) { - containsDuplicateFeatures = true; - } - return featureIsNew; - } - @Override public List getFeatures() { return Collections.unmodifiableList(features); } - /** - * Adds a new identity of the requested entity to the discovered information. - * - * @param identity the discovered entity's identity - * @deprecated use {@link DiscoverInfoBuilder#addIdentity(DiscoverInfo.Identity)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public void addIdentity(Identity identity) { - identities.add(identity); - identitiesSet.add(identity.getKey()); - } - - /** - * Adds identities to the DiscoverInfo stanza. - * - * @param identitiesToAdd TODO javadoc me please - * @deprecated use {@link DiscoverInfoBuilder#addIdentities(Collection)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public void addIdentities(Collection identitiesToAdd) { - if (identitiesToAdd == null) return; - for (Identity identity : identitiesToAdd) { - addIdentity(identity); - } - } - @Override public List getIdentities() { return Collections.unmodifiableList(identities); @@ -215,7 +128,7 @@ public class DiscoverInfo extends IQ implements DiscoverInfoView { * * @param category category the category to look for. * @param type type the type to look for. - * @return a list of Identites with the given category and type. + * @return a list of Identities with the given category and type. */ public List getIdentities(String category, String type) { List res = new ArrayList<>(identities.size()); @@ -232,22 +145,6 @@ public class DiscoverInfo extends IQ implements DiscoverInfoView { return node; } - /** - * Sets the node attribute that supplements the 'jid' attribute. A node is merely - * something that is associated with a JID and for which the JID can provide information.

      - * - * Node attributes SHOULD be used only when trying to provide or query information which - * is not directly addressable. - * - * @param node the node attribute that supplements the 'jid' attribute - * @deprecated use {@link DiscoverInfoBuilder#setNode(String)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public void setNode(String node) { - this.node = StringUtils.requireNullOrNotEmpty(node, "The node can not be the empty string"); - } - /** * Returns true if the specified feature is part of the discovered information. * @@ -286,7 +183,7 @@ public class DiscoverInfo extends IQ implements DiscoverInfoView { * @return true if duplicate identities where found, otherwise false */ public boolean containsDuplicateIdentities() { - List checkedIdentities = new LinkedList<>(); + List checkedIdentities = new ArrayList<>(identities.size()); for (Identity i : identities) { for (Identity i2 : checkedIdentities) { if (i.equals(i2)) @@ -310,18 +207,6 @@ public class DiscoverInfo extends IQ implements DiscoverInfoView { return new DiscoverInfoBuilder(this, stanzaId); } - /** - * Deprecated, do not use. - * - * @deprecated use {@link #asBuilder(String)} instead. - */ - // TODO: Remove in Smack 4.5. - @Deprecated - @Override - public DiscoverInfo clone() { - return new DiscoverInfo(this); - } - public static DiscoverInfoBuilder builder(XMPPConnection connection) { return new DiscoverInfoBuilder(connection); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverItems.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverItems.java index 34c62a8a1..a6394716c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverItems.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverItems.java @@ -16,9 +16,9 @@ */ package org.jivesoftware.smackx.disco.packet; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import org.jivesoftware.smack.packet.IQ; @@ -40,7 +40,7 @@ public class DiscoverItems extends IQ { public static final String ELEMENT = QUERY_ELEMENT; public static final String NAMESPACE = "http://jabber.org/protocol/disco#items"; - private final List items = new LinkedList<>(); + private final List items = new ArrayList<>(); private String node; public DiscoverItems() { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IncomingFileTransfer.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IncomingFileTransfer.java index c09e9fcb2..9a17438d0 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IncomingFileTransfer.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IncomingFileTransfer.java @@ -51,7 +51,7 @@ import org.jivesoftware.smack.util.CloseableUtil; * The second way that a file can be received through this class is by invoking * the {@link #receiveFile(File)} method. This method returns immediately and * takes as its parameter a file on the local file system where the file - * recieved from the transfer will be put. + * received from the transfer will be put. * * @author Alexander Wenckus */ diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/StreamNegotiator.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/StreamNegotiator.java index adf7297e5..0fc025827 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/StreamNegotiator.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/StreamNegotiator.java @@ -52,7 +52,7 @@ public abstract class StreamNegotiator extends Manager { } /** - * A event manager for stream initiation requests send to us. + * An event manager for stream initiation requests send to us. *

      * Those are typical XEP-45 Open or XEP-65 Bytestream IQ requests. The even key is in the format * "initiationFrom + '\t' + streamId" diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/package-info.java index 505394d1e..a05f6d56c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/package-info.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/package-info.java @@ -144,7 +144,7 @@ *

    • **isDone()** - Similar to getProgress() except it returns a _boolean_. If the state is rejected, canceled, error, * or complete then true will be returned and false otherwise.
    • *
    • **getError()** - If there is an error during the file transfer this method will return the type of error that - * occured.
    • + * occurred. *
    *

    Examples

    *

    diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/formtypes/FormFieldRegistry.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/formtypes/FormFieldRegistry.java index 5dda27842..9dae0e1d5 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/formtypes/FormFieldRegistry.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/formtypes/FormFieldRegistry.java @@ -94,7 +94,13 @@ public class FormFieldRegistry { } else { previousType = fieldNameToType.get(fieldName); if (previousType != null && previousType != fieldType) { - throw new IllegalArgumentException(); + String message = "The field '" + fieldName + "' from form type '" + formType + + "' was already registered with field type '" + previousType + + "' while it is now seen with type '" + fieldType + + "'. Form field types have to be unambigiously." + + " XMPP uses a registry for form field tpes, scoped by the form type." + + " You may find the correct value at https://xmpp.org/registrar/formtypes.html"; + throw new IllegalArgumentException(message); } } previousType = fieldNameToType.put(fieldName, fieldType); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/geoloc/GeoLocationManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/geoloc/GeoLocationManager.java index f29306251..eb91bec55 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/geoloc/GeoLocationManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/geoloc/GeoLocationManager.java @@ -43,7 +43,7 @@ import org.jxmpp.jid.Jid; *
    * To publish a UserLocation, please use {@link #publishGeoLocation(GeoLocation)} method. This will publish the node. *
    - * To stop publishing a UserLocation, please use {@link #stopPublishingGeolocation()} method. This will send a disble publishing signal. + * To stop publishing a UserLocation, please use {@link #stopPublishingGeolocation()} method. This will send a disable publishing signal. *
    * To add a {@link PepEventListener} in order to remain updated with other users GeoLocation, use {@link #addGeoLocationListener(PepEventListener)} method. *
    diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqlast/packet/LastActivity.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqlast/packet/LastActivity.java index 08d3ac686..9a94d0198 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqlast/packet/LastActivity.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqlast/packet/LastActivity.java @@ -45,11 +45,13 @@ public class LastActivity extends IQ { public long lastActivity = -1; public String message; + @SuppressWarnings("this-escape") public LastActivity() { super(ELEMENT, NAMESPACE); setType(IQ.Type.get); } + @SuppressWarnings("this-escape") public LastActivity(Jid to) { this(); setTo(to); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqprivate/packet/PrivateData.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqprivate/packet/PrivateData.java index 058b0c2d5..c6670c900 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqprivate/packet/PrivateData.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqprivate/packet/PrivateData.java @@ -41,7 +41,7 @@ public interface PrivateData { String getNamespace(); /** - * Returns the XML reppresentation of the PrivateData. + * Returns the XML representation of the PrivateData. * * @return the private data as XML. */ diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqprivate/packet/PrivateDataIQ.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqprivate/packet/PrivateDataIQ.java index 6063e040e..5df357e09 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqprivate/packet/PrivateDataIQ.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqprivate/packet/PrivateDataIQ.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014 Florian Schmaus + * Copyright 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,11 +27,13 @@ public class PrivateDataIQ extends IQ { private final String getElement; private final String getNamespace; + @SuppressWarnings("this-escape") public PrivateDataIQ(PrivateData privateData) { this(privateData, null, null); setType(Type.set); } + @SuppressWarnings("this-escape") public PrivateDataIQ(String element, String namespace) { this(null, element, namespace); setType(Type.get); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqregister/AccountManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqregister/AccountManager.java index 7a991978c..c6f32970c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqregister/AccountManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqregister/AccountManager.java @@ -354,15 +354,17 @@ public final class AccountManager extends Manager { /** * Gets the account registration info from the server. * + * @return Registration information * @throws XMPPErrorException if there was an XMPP error returned. * @throws NoResponseException if there was no response from the remote entity. * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - private synchronized void getRegistrationInfo() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + public synchronized Registration getRegistrationInfo() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { Registration reg = new Registration(); reg.setTo(connection().getXMPPServiceDomain()); info = createStanzaCollectorAndSend(reg).nextResultOrThrow(); + return info; } private StanzaCollector createStanzaCollectorAndSend(IQ req) throws NotConnectedException, InterruptedException { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqregister/provider/RegistrationProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqregister/provider/RegistrationProvider.java index 67da11bbe..d087a5bf4 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqregister/provider/RegistrationProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqregister/provider/RegistrationProvider.java @@ -17,8 +17,8 @@ package org.jivesoftware.smackx.iqregister.provider; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -40,7 +40,7 @@ public class RegistrationProvider extends IqProvider { public Registration parse(XmlPullParser parser, int initialDepth, IqData iqData, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { String instruction = null; Map fields = new HashMap<>(); - List packetExtensions = new LinkedList<>(); + List packetExtensions = new ArrayList<>(); outerloop: while (true) { XmlPullParser.Event eventType = parser.next(); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqversion/packet/Version.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqversion/packet/Version.java index b0dd5c1d0..fb0ca01fa 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqversion/packet/Version.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqversion/packet/Version.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2021 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2021-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ import org.jivesoftware.smack.util.StringUtils; /** * A Version IQ packet, which is used by XMPP clients to discover version information - * about the software running at another entity's JID.

    + * about the software running at another entity's JID. * * @author Gaston Dombiak */ diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java index ea36bcc31..4fb09922c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java @@ -52,7 +52,7 @@ import org.jxmpp.jid.FullJid; * | transport-info * | transport-reject * | transport-replace - * │ initator (RECOMMENDED for session initiate, NOT RECOMMENDED otherwise, full JID, XEP-0166 § 7.1) + * │ initiator (RECOMMENDED for session initiate, NOT RECOMMENDED otherwise, full JID, XEP-0166 § 7.1) * │ responder (RECOMMENDED for session accept, NOT RECOMMENDED otherwise, full JID. XEP-0166 § 7.1) * │ sid (REQUIRED, SHOULD match XML Nmtoken production) * │ diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContent.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContent.java index ab5680c1d..702183dd2 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContent.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContent.java @@ -71,7 +71,7 @@ public final class JingleContent implements XmlElement { private final JingleContentTransport transport; /** - * Creates a content description.. + * Creates a content description. */ private JingleContent(Creator creator, String disposition, String name, Senders senders, JingleContentDescription description, JingleContentTransport transport) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportManager.java index 8c91c8976..ba344ee7c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportManager.java @@ -30,6 +30,7 @@ public abstract class JingleTransportManager i private final XMPPConnection connection; + @SuppressWarnings("this-escape") public JingleTransportManager(XMPPConnection connection) { this.connection = connection; connection.addConnectionListener(this); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/elements/JingleS5BTransportCandidate.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/elements/JingleS5BTransportCandidate.java index 4ad96fcca..d3be5a4b3 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/elements/JingleS5BTransportCandidate.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/elements/JingleS5BTransportCandidate.java @@ -51,7 +51,7 @@ public final class JingleS5BTransportCandidate extends JingleContentTransportCan private final Type type; public JingleS5BTransportCandidate(String candidateId, String hostString, Jid jid, int port, int priority, Type type) { - this(candidateId, InternetAddress.from(hostString), jid, port, priority, type); + this(candidateId, InternetAddress.fromIgnoringZoneId(hostString), jid, port, priority, type); } public JingleS5BTransportCandidate(String candidateId, InternetAddress host, Jid jid, int port, int priority, Type type) { @@ -176,7 +176,7 @@ public final class JingleS5BTransportCandidate extends JingleContentTransportCan } public Builder setHost(String host) { - InternetAddress inetAddress = InternetAddress.from(host); + InternetAddress inetAddress = InternetAddress.fromIgnoringZoneId(host); return setHost(inetAddress); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/JivePropertiesManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/JivePropertiesManager.java index f02f56c8e..09bf70c5b 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/JivePropertiesManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/JivePropertiesManager.java @@ -1,6 +1,6 @@ /** * - * Copyright 2014 Florian Schmaus. + * Copyright 2014-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,25 +50,6 @@ public class JivePropertiesManager { return javaObjectEnabled; } - /** - * Convenience method to add a property to a packet. - * - * @param packet the stanza to add the property to. - * @param name the name of the property to add. - * @param value the value of the property to add. - * @deprecated use {@link #addProperty(StanzaBuilder, String, Object)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public static void addProperty(Stanza packet, String name, Object value) { - JivePropertiesExtension jpe = (JivePropertiesExtension) packet.getExtension(JivePropertiesExtension.NAMESPACE); - if (jpe == null) { - jpe = new JivePropertiesExtension(); - packet.addExtension(jpe); - } - jpe.setProperty(name, value); - } - /** * Convenience method to add a property to a stanza. * diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/package-info.java index 45a9fe3e9..a5ea271a5 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/package-info.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/package-info.java @@ -70,7 +70,7 @@ chat.sendMessage(message); *

      *
    • When you send a Java object as a property, only clients running Java will be able to interpret the data. So, * consider using a series of primitive values to transfer data instead.
    • - *
    • Objects sent as property values must implement Serialiable. Additionally, both the sender and receiver must have + *
    • Objects sent as property values must implement Serializable. Additionally, both the sender and receiver must have * identical versions of the class, or a serialization exception will occur when de-serializing the object.
    • *
    • Serialized objects can potentially be quite large, which will use more bandwidth and server resources.
    • *
    diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/provider/JivePropertiesExtensionProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/provider/JivePropertiesExtensionProvider.java index 6d99c1637..8326e93e5 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/provider/JivePropertiesExtensionProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/provider/JivePropertiesExtensionProvider.java @@ -46,7 +46,7 @@ public class JivePropertiesExtensionProvider extends ExtensionElementProvider - * Note that you have to explicitly enabled Java object deserialization with @{link + * Note that you have to explicitly enabled Java object deserialization with * {@link JivePropertiesManager#setJavaObjectEnabled(boolean)} * * @param parser the XML parser, positioned at the start of a properties sub-packet. diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/last_interaction/element/IdleElement.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/last_interaction/element/IdleElement.java index 2799c1224..6603fe21b 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/last_interaction/element/IdleElement.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/last_interaction/element/IdleElement.java @@ -37,6 +37,7 @@ public class IdleElement implements ExtensionElement { /** * Create a new IdleElement with the current date as date of last user interaction. */ + @SuppressWarnings("JavaUtilDate") public IdleElement() { this(new Date()); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/Affiliate.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/Affiliate.java index 3e3648f26..d3e034c6c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/Affiliate.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/Affiliate.java @@ -55,10 +55,10 @@ public class Affiliate { } /** - * Returns the affiliation of the afffiliated user. Possible affiliations are: "owner", "admin", + * Returns the affiliation of the affiliated user. Possible affiliations are: "owner", "admin", * "member", "outcast". This information will always be available. * - * @return the affiliation of the afffiliated user. + * @return the affiliation of the affiliated user. */ public MUCAffiliation getAffiliation() { return affiliation; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/DefaultParticipantStatusListener.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/DefaultParticipantStatusListener.java deleted file mode 100644 index c9d809cd2..000000000 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/DefaultParticipantStatusListener.java +++ /dev/null @@ -1,98 +0,0 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.muc; - -import org.jxmpp.jid.EntityFullJid; -import org.jxmpp.jid.Jid; -import org.jxmpp.jid.parts.Resourcepart; - -/** - * Default implementation of the ParticipantStatusListener interface.

    - * - * This class does not provide any behavior by default. It just avoids having - * to implement all the inteface methods if the user is only interested in implementing - * some of the methods. - * - * @author Gaston Dombiak - * @deprecated use {@link ParticipantStatusListener} instead. - */ -// TODO: Remove in Smack 4.5 -@Deprecated -public class DefaultParticipantStatusListener implements ParticipantStatusListener { - - @Override - public void joined(EntityFullJid participant) { - } - - @Override - public void left(EntityFullJid participant) { - } - - @Override - public void kicked(EntityFullJid participant, Jid actor, String reason) { - } - - @Override - public void voiceGranted(EntityFullJid participant) { - } - - @Override - public void voiceRevoked(EntityFullJid participant) { - } - - @Override - public void banned(EntityFullJid participant, Jid actor, String reason) { - } - - @Override - public void membershipGranted(EntityFullJid participant) { - } - - @Override - public void membershipRevoked(EntityFullJid participant) { - } - - @Override - public void moderatorGranted(EntityFullJid participant) { - } - - @Override - public void moderatorRevoked(EntityFullJid participant) { - } - - @Override - public void ownershipGranted(EntityFullJid participant) { - } - - @Override - public void ownershipRevoked(EntityFullJid participant) { - } - - @Override - public void adminGranted(EntityFullJid participant) { - } - - @Override - public void adminRevoked(EntityFullJid participant) { - } - - @Override - public void nicknameChanged(EntityFullJid participant, Resourcepart newNickname) { - } - -} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/DefaultUserStatusListener.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/DefaultUserStatusListener.java deleted file mode 100644 index 0ac93ccbd..000000000 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/DefaultUserStatusListener.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * - * Copyright 2003-2007 Jive Software. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jivesoftware.smackx.muc; - -import org.jxmpp.jid.Jid; - -/** - * Default implementation of the UserStatusListener interface.

    - * - * This class does not provide any behavior by default. It just avoids having - * to implement all the inteface methods if the user is only interested in implementing - * some of the methods. - * - * @author Gaston Dombiak - * @deprecated use {@link UserStatusListener} instead. - */ -// TODO: Remove in Smack 4.5. -@Deprecated -public class DefaultUserStatusListener implements UserStatusListener { - - @Override - public void kicked(Jid actor, String reason) { - } - - @Override - public void voiceGranted() { - } - - @Override - public void voiceRevoked() { - } - - @Override - public void banned(Jid actor, String reason) { - } - - @Override - public void membershipGranted() { - } - - @Override - public void membershipRevoked() { - } - - @Override - public void moderatorGranted() { - } - - @Override - public void moderatorRevoked() { - } - - @Override - public void ownershipGranted() { - } - - @Override - public void ownershipRevoked() { - } - - @Override - public void adminGranted() { - } - - @Override - public void adminRevoked() { - } - - @Override - public void roomDestroyed(MultiUserChat alternateMUC, String reason) { - } - -} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucConfigFormManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucConfigFormManager.java index 1735d0b4f..b847cd025 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucConfigFormManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucConfigFormManager.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2020 Florian Schmaus + * Copyright 2015-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smackx.muc.MultiUserChatException.MucConfigurationNotSupportedException; +import org.jivesoftware.smackx.xdata.BooleanFormField; import org.jivesoftware.smackx.xdata.FormField; import org.jivesoftware.smackx.xdata.form.FillableForm; import org.jivesoftware.smackx.xdata.form.FilledForm; @@ -61,6 +62,13 @@ public class MucConfigFormManager { */ public static final String MUC_ROOMCONFIG_ROOMOWNERS = "muc#roomconfig_roomowners"; + /** + * The constant String {@value}. + * + * @see XEP-0045 § 10. Owner Use Cases + */ + public static final String MUC_ROOMCONFIG_ROOMADMINS = "muc#roomconfig_roomadmins"; + /** * The constant String {@value}. */ @@ -88,10 +96,25 @@ public class MucConfigFormManager { */ public static final String MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM = "muc#roomconfig_publicroom"; + /** + * The constant String {@value}. + */ + public static final String MUC_ROOMCONFIG_ROOMNAME = "muc#roomconfig_roomname"; + + /** + * The constant String {@value}. + */ + public static final String MUC_ROOMCONFIG_ENABLE_PUBLIC_LOGGING = "muc#roomconfig_enablelogging"; + + /** + * The constant String {@value}. + */ + public static final String MUC_ROOMCONFIG_CHANGE_SUBJECT = "muc#roomconfig_changesubject"; private final MultiUserChat multiUserChat; private final FillableForm answerForm; private final List owners; + private final List admins; /** * Create a new MUC config form manager. @@ -125,6 +148,18 @@ public class MucConfigFormManager { // roomowners not supported, this should barely be the case owners = null; } + + FormField roomAdminsFormField = answerForm.getDataForm().getField(MUC_ROOMCONFIG_ROOMADMINS); + if (roomAdminsFormField != null) { + // Set 'admins' to the currently configured admins + List adminStrings = roomAdminsFormField.getValues(); + admins = new ArrayList<>(adminStrings.size()); + JidUtil.jidsFrom(adminStrings, admins, null); + } + else { + // roomadmins not supported, this should barely be the case + admins = null; + } } /** @@ -136,6 +171,15 @@ public class MucConfigFormManager { return owners != null; } + /** + * Check if the room supports room admins. + * @return true if supported, false if not. + * @see #MUC_ROOMCONFIG_ROOMADMINS + */ + public boolean supportsRoomAdmins() { + return admins != null; + } + /** * Set the owners of the room. * @@ -153,6 +197,23 @@ public class MucConfigFormManager { return this; } + /** + * Set the admins of the room. + * + * @param newAdmins a collection of JIDs to become the new admins of the room. + * @return a reference to this object. + * @throws MucConfigurationNotSupportedException if the MUC service does not support this option. + * @see #MUC_ROOMCONFIG_ROOMADMINS + */ + public MucConfigFormManager setRoomAdmins(Collection newAdmins) throws MucConfigurationNotSupportedException { + if (!supportsRoomAdmins()) { + throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ROOMADMINS); + } + admins.clear(); + admins.addAll(newAdmins); + return this; + } + /** * Check if the room supports a members only configuration. * @@ -223,6 +284,15 @@ public class MucConfigFormManager { } + /** + * Check if the room supports its visibility being controlled via configuration. + * + * @return true if supported, false if not. + */ + public boolean supportsPublicRoom() { + return answerForm.hasField(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM); + } + /** * Make the room publicly searchable. * @@ -251,13 +321,25 @@ public class MucConfigFormManager { * @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service. */ public MucConfigFormManager setPublic(boolean isPublic) throws MucConfigurationNotSupportedException { - if (!supportsModeration()) { + if (!supportsPublicRoom()) { throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM); } answerForm.setAnswer(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM, isPublic); return this; } + public boolean supportsRoomname() { + return answerForm.hasField(MUC_ROOMCONFIG_ROOMNAME); + } + + public MucConfigFormManager setRoomName(String roomName) throws MucConfigurationNotSupportedException { + if (!supportsRoomname()) { + throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ROOMNAME); + } + answerForm.setAnswer(MUC_ROOMCONFIG_ROOMNAME, roomName); + return this; + } + /** * Check if the room supports password protection. * @@ -299,13 +381,33 @@ public class MucConfigFormManager { */ public MucConfigFormManager setIsPasswordProtected(boolean isPasswordProtected) throws MucConfigurationNotSupportedException { - if (!supportsMembersOnly()) { + if (!supportsPasswordProtected()) { throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM); } answerForm.setAnswer(MUC_ROOMCONFIG_PASSWORDPROTECTEDROOM, isPasswordProtected); return this; } + public boolean supportsPublicLogging() { + return answerForm.hasField(MUC_ROOMCONFIG_ENABLE_PUBLIC_LOGGING); + } + + public MucConfigFormManager setPublicLogging(boolean enabled) throws MucConfigurationNotSupportedException { + if (!supportsPublicLogging()) { + throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_ENABLE_PUBLIC_LOGGING); + } + answerForm.setAnswer(MUC_ROOMCONFIG_ENABLE_PUBLIC_LOGGING, enabled); + return this; + } + + public MucConfigFormManager enablePublicLogging() throws MucConfigurationNotSupportedException { + return setPublicLogging(true); + } + + public MucConfigFormManager disablPublicLogging() throws MucConfigurationNotSupportedException { + return setPublicLogging(false); + } + /** * Set the room secret, aka the room password. If set and enabled, the password is required to * join the room. Note that this does only set it by does not enable password protection. Use @@ -324,6 +426,33 @@ public class MucConfigFormManager { return this; } + public boolean supportsChangeSubjectByOccupant() { + return answerForm.hasField(MUC_ROOMCONFIG_CHANGE_SUBJECT); + } + + public boolean occupantsAreAllowedToChangeSubject() throws MucConfigurationNotSupportedException { + if (!supportsChangeSubjectByOccupant()) { + throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_CHANGE_SUBJECT); + } + return answerForm.getField(MUC_ROOMCONFIG_CHANGE_SUBJECT).ifPossibleAsOrThrow(BooleanFormField.class).getValueAsBoolean(); + } + + public MucConfigFormManager setChangeSubjectByOccupant(boolean enabled) throws MucConfigurationNotSupportedException { + if (!supportsChangeSubjectByOccupant()) { + throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_CHANGE_SUBJECT); + } + answerForm.setAnswer(MUC_ROOMCONFIG_CHANGE_SUBJECT, enabled); + return this; + } + + public MucConfigFormManager allowOccupantsToChangeSubject() throws MucConfigurationNotSupportedException { + return setChangeSubjectByOccupant(true); + } + + public MucConfigFormManager disallowOccupantsToChangeSubject() throws MucConfigurationNotSupportedException { + return setChangeSubjectByOccupant(false); + } + /** * Submit the configuration as {@link FilledForm} to the room. * @@ -337,6 +466,9 @@ public class MucConfigFormManager { if (owners != null) { answerForm.setAnswer(MUC_ROOMCONFIG_ROOMOWNERS, JidUtil.toStringList(owners)); } + if (admins != null) { + answerForm.setAnswer(MUC_ROOMCONFIG_ROOMADMINS, JidUtil.toStringList(admins)); + } multiUserChat.sendConfigurationForm(answerForm); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucEnterConfiguration.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucEnterConfiguration.java index ef3e17ba7..58c0c4e26 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucEnterConfiguration.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucEnterConfiguration.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2020 Florian Schmaus + * Copyright 2015-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,13 +60,8 @@ public final class MucEnterConfiguration { since = builder.since; timeout = builder.timeout; - final PresenceBuilder joinPresenceBuilder; - if (builder.joinPresence == null) { - joinPresenceBuilder = builder.joinPresenceBuilder.ofType(Presence.Type.available); - } - else { - joinPresenceBuilder = builder.joinPresence.asBuilder(); - } + final PresenceBuilder joinPresenceBuilder = builder.joinPresenceBuilder.ofType(Presence.Type.available); + // Indicate the client supports MUC joinPresenceBuilder.addExtension(new MUCInitialPresence(password, maxChars, maxStanzas, seconds, since)); @@ -95,9 +90,6 @@ public final class MucEnterConfiguration { private final PresenceBuilder joinPresenceBuilder; - // TODO: Remove in Smack 4.5. - private Presence joinPresence; - Builder(Resourcepart nickname, XMPPConnection connection) { this.nickname = Objects.requireNonNull(nickname, "Nickname must not be null"); @@ -109,31 +101,7 @@ public final class MucEnterConfiguration { /** * Set the presence used to join the MUC room. - *

    - * The 'to' value of the given presence will be overridden and the given presence must be of type - * 'available', otherwise an {@link IllegalArgumentException} will be thrown. - *

    - * - * @param presence TODO javadoc me please - * @return a reference to this builder. - * @deprecated use {@link #withPresence(Consumer)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public Builder withPresence(Presence presence) { - if (presence.getType() != Presence.Type.available) { - throw new IllegalArgumentException("Presence must be of type 'available'"); - } - - joinPresence = presence; - return this; - } - - /** - * Set the presence used to join the MUC room. - *

    * The consumer must not modify the presence type, otherwise an {@link IllegalArgumentException} will be thrown. - *

    * * @param presenceBuilderConsumer a consumer which will be passed the presence build. * @return a reference to this builder. diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java index b7fd3ad97..e71f6b523 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software. 2020-2022 Florian Schmaus + * Copyright 2003-2007 Jive Software. 2020-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,10 +87,10 @@ import org.jivesoftware.smackx.xdata.form.FillableForm; import org.jivesoftware.smackx.xdata.form.Form; import org.jivesoftware.smackx.xdata.packet.DataForm; +import org.jxmpp.jid.BareJid; import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityFullJid; -import org.jxmpp.jid.EntityJid; import org.jxmpp.jid.Jid; import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.parts.Resourcepart; @@ -258,13 +258,14 @@ public class MultiUserChat { break; case unavailable: occupantsMap.remove(from); - if (mucUser != null && mucUser.hasStatus()) { - if (isUserStatusModification) { + Set status = mucUser.getStatus(); + if (mucUser != null && !status.isEmpty()) { + if (isUserStatusModification && !status.contains(MUCUser.Status.NEW_NICKNAME_303)) { userHasLeft(); } // Fire events according to the received presence code checkPresenceCode( - mucUser.getStatus(), + status, isUserStatusModification, mucUser, from); @@ -289,8 +290,9 @@ public class MultiUserChat { } for (UserStatusListener listener : userStatusListeners) { - listener.roomDestroyed(alternateMuc, destroy.getReason()); + listener.roomDestroyed(alternateMuc, destroy.getPassword(), destroy.getReason()); } + userHasLeft(); } if (isUserStatusModification) { @@ -350,7 +352,7 @@ public class MultiUserChat { * @throws NoResponseException if there was no response from the remote entity. * @throws XMPPErrorException if there was an XMPP error returned. * @throws InterruptedException if the calling thread was interrupted. - * @throws NotAMucServiceException if the entity is not a MUC serivce. + * @throws NotAMucServiceException if the entity is not a MUC service. * @see XEP-45 7.2 Entering a Room */ private Presence enter(MucEnterConfiguration conf) throws NotConnectedException, NoResponseException, @@ -364,7 +366,7 @@ public class MultiUserChat { // field is in the form "roomName@service/nickname" Presence joinPresence = conf.getJoinPresence(this); - // Setup the messageListeners and presenceListeners *before* the join presence is send. + // Set up the messageListeners and presenceListeners *before* the join presence is sent. connection.addStanzaListener(messageListener, fromRoomGroupchatFilter); StanzaFilter presenceFromRoomFilter = new AndFilter(fromRoomFilter, StanzaTypeFilter.PRESENCE, @@ -400,12 +402,12 @@ public class MultiUserChat { StanzaCollector presenceStanzaCollector = null; final Presence reflectedSelfPresence; try { - // This stanza collector will collect the final self presence from the MUC, which also signals that we have successful entered the MUC. + // This stanza collector will collect the final self presence from the MUC, which also signals that we have successfully entered the MUC. StanzaCollector selfPresenceCollector = connection.createStanzaCollectorAndSend(responseFilter, joinPresence); - StanzaCollector.Configuration presenceStanzaCollectorConfguration = StanzaCollector.newConfiguration().setCollectorToReset( + StanzaCollector.Configuration presenceStanzaCollectorConfiguration = StanzaCollector.newConfiguration().setCollectorToReset( selfPresenceCollector).setStanzaFilter(presenceFromRoomFilter); // This stanza collector is used to reset the timeout of the selfPresenceCollector. - presenceStanzaCollector = connection.createStanzaCollector(presenceStanzaCollectorConfguration); + presenceStanzaCollector = connection.createStanzaCollector(presenceStanzaCollectorConfiguration); reflectedSelfPresence = selfPresenceCollector.nextResultOrThrow(conf.getTimeout()); } catch (NotConnectedException | InterruptedException | NoResponseException | XMPPErrorException e) { @@ -422,15 +424,15 @@ public class MultiUserChat { synchronized (presenceListener) { // Only continue after we have received *and* processed the reflected self-presence. Since presences are // handled in an extra listener, we may return from enter() without having processed all presences of the - // participants, resulting in a e.g. to low participant counter after enter(). Hence we wait here until the + // participants, resulting in a e.g. to low participant counter after enter(). Hence, we wait here until the // processing is done. while (!processedReflectedSelfPresence) { presenceListener.wait(); } } - // This presence must be send from a full JID. We use the resourcepart of this JID as nick, since the room may - // performed roomnick rewriting + // This presence must be sent from a full JID. We use the resourcepart of this JID as nick, since the room may + // have performed roomnick rewriting Resourcepart receivedNickname = reflectedSelfPresence.getFrom().getResourceOrThrow(); setNickname(receivedNickname); @@ -480,7 +482,7 @@ public class MultiUserChat { * @throws NotConnectedException if the XMPP connection is not connected. * @throws MucAlreadyJoinedException if already joined the Multi-User Chat.7y * @throws MissingMucCreationAcknowledgeException if there MUC creation was not acknowledged by the service. - * @throws NotAMucServiceException if the entity is not a MUC serivce. + * @throws NotAMucServiceException if the entity is not a MUC service. */ public synchronized MucCreateConfigFormHandle create(Resourcepart nickname) throws NoResponseException, XMPPErrorException, InterruptedException, MucAlreadyJoinedException, @@ -514,7 +516,7 @@ public class MultiUserChat { * @throws InterruptedException if the calling thread was interrupted. * @throws NotConnectedException if the XMPP connection is not connected. * @throws MucAlreadyJoinedException if already joined the Multi-User Chat.7y - * @throws NotAMucServiceException if the entity is not a MUC serivce. + * @throws NotAMucServiceException if the entity is not a MUC service. */ public synchronized MucCreateConfigFormHandle createOrJoin(Resourcepart nickname) throws NoResponseException, XMPPErrorException, InterruptedException, MucAlreadyJoinedException, NotConnectedException, NotAMucServiceException { @@ -536,7 +538,7 @@ public class MultiUserChat { * @throws InterruptedException if the calling thread was interrupted. * @throws MucAlreadyJoinedException if the MUC is already joined * @throws NotConnectedException if the XMPP connection is not connected. - * @throws NotAMucServiceException if the entity is not a MUC serivce. + * @throws NotAMucServiceException if the entity is not a MUC service. */ public synchronized MucCreateConfigFormHandle createOrJoin(MucEnterConfiguration mucEnterConfiguration) throws NoResponseException, XMPPErrorException, InterruptedException, MucAlreadyJoinedException, NotConnectedException, NotAMucServiceException { @@ -609,7 +611,7 @@ public class MultiUserChat { * @throws XMPPErrorException if there was an XMPP error returned. * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. - * @throws NotAMucServiceException if the entity is not a MUC serivce. + * @throws NotAMucServiceException if the entity is not a MUC service. */ public MucCreateConfigFormHandle createOrJoinIfNecessary(Resourcepart nickname, String password) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotAMucServiceException { @@ -645,7 +647,7 @@ public class MultiUserChat { * @throws NoResponseException if there was no response from the server. * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. - * @throws NotAMucServiceException if the entity is not a MUC serivce. + * @throws NotAMucServiceException if the entity is not a MUC service. */ public Presence join(Resourcepart nickname) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotAMucServiceException { @@ -675,7 +677,7 @@ public class MultiUserChat { * @throws InterruptedException if the calling thread was interrupted. * @throws NotConnectedException if the XMPP connection is not connected. * @throws NoResponseException if there was no response from the server. - * @throws NotAMucServiceException if the entity is not a MUC serivce. + * @throws NotAMucServiceException if the entity is not a MUC service. */ public void join(Resourcepart nickname, String password) throws XMPPErrorException, InterruptedException, NoResponseException, NotConnectedException, NotAMucServiceException { MucEnterConfiguration.Builder builder = getEnterConfigurationBuilder(nickname).withPassword( @@ -708,7 +710,7 @@ public class MultiUserChat { * @throws NoResponseException if there was no response from the server. * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. - * @throws NotAMucServiceException if the entity is not a MUC serivce. + * @throws NotAMucServiceException if the entity is not a MUC service. */ public synchronized Presence join(MucEnterConfiguration mucEnterConfiguration) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException, NotAMucServiceException { @@ -716,7 +718,7 @@ public class MultiUserChat { // nickname. if (isJoined()) { try { - leaveSync(); + leave(); } catch (XMPPErrorException | NoResponseException | MucNotJoinedException e) { LOGGER.log(Level.WARNING, "Could not leave MUC prior joining, assuming we are not joined", e); @@ -736,23 +738,6 @@ public class MultiUserChat { return getMyRoomJid() != null; } - /** - * Leave the chat room. - * - * @return the leave presence as reflected by the MUC. - * @throws NotConnectedException if the XMPP connection is not connected. - * @throws InterruptedException if the calling thread was interrupted. - * @throws XMPPErrorException if there was an XMPP error returned. - * @throws NoResponseException if there was no response from the remote entity. - * @throws MucNotJoinedException if not joined to the Multi-User Chat. - * @deprecated use {@link #leave()} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public synchronized Presence leaveSync() throws NotConnectedException, InterruptedException, MucNotJoinedException, NoResponseException, XMPPErrorException { - return leave(); - } - /** * Leave the chat room. * @@ -832,9 +817,9 @@ public class MultiUserChat { /** * Returns the room's configuration form that the room's owner can use. * The configuration form allows to set the room's language, - * enable logging, specify room's type, etc.. + * enable logging, specify room's type, etc. * - * @return the Form that contains the fields to complete together with the instrucions or + * @return the Form that contains the fields to complete together with the instructions or * null if no configuration is possible. * @throws XMPPErrorException if an error occurs asking the configuration form for the room. * @throws NoResponseException if there was no response from the server. @@ -888,7 +873,7 @@ public class MultiUserChat { * error to the user (error code 405). * * @return the registration Form that contains the fields to complete together with the - * instrucions or null if no registration is possible. + * instructions or null if no registration is possible. * @throws XMPPErrorException if an error occurs asking the registration form for the room or a * 405 error if the user is not allowed to register with the room. * @throws NoResponseException if there was no response from the server. @@ -964,12 +949,32 @@ public class MultiUserChat { * @throws InterruptedException if the calling thread was interrupted. */ public void destroy(String reason, EntityBareJid alternateJID) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + destroy(reason, alternateJID, null); + } + + /** + * Sends a request to the server to destroy the room. The sender of the request + * should be the room's owner. If the sender of the destroy request is not the room's owner + * then the server will answer a "Forbidden" error (403). + * + * @param reason an optional reason for the room destruction. + * @param alternateJID an optional JID of an alternate location. + * @param password an optional password for the alternate location + * @throws XMPPErrorException if an error occurs while trying to destroy the room. + * An error can occur which will be wrapped by an XMPPException -- + * XMPP error code 403. The error code can be used to present more + * appropriate error messages to end-users. + * @throws NoResponseException if there was no response from the server. + * @throws NotConnectedException if the XMPP connection is not connected. + * @throws InterruptedException if the calling thread was interrupted. + */ + public void destroy(String reason, EntityBareJid alternateJID, String password) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { MUCOwner iq = new MUCOwner(); iq.setTo(room); iq.setType(IQ.Type.set); // Create the reason for the room destruction - Destroy destroy = new Destroy(alternateJID, reason); + Destroy destroy = new Destroy(alternateJID, password, reason); iq.setDestroy(destroy); try { @@ -1006,36 +1011,6 @@ public class MultiUserChat { invite(connection.getStanzaFactory().buildMessageStanza(), user, reason); } - /** - * Invites another user to the room in which one is an occupant using a given Message. The invitation - * will be sent to the room which in turn will forward the invitation to the invitee.

    - * - * If the room is password-protected, the invitee will receive a password to use to join - * the room. If the room is members-only, the invitee may be added to the member list. - * - * @param message the message to use for sending the invitation. - * @param user the user to invite to the room.(e.g. hecate@shakespeare.lit) - * @param reason the reason why the user is being invited. - * @throws NotConnectedException if the XMPP connection is not connected. - * @throws InterruptedException if the calling thread was interrupted. - * @deprecated use {@link #invite(MessageBuilder, EntityBareJid, String)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public void invite(Message message, EntityBareJid user, String reason) throws NotConnectedException, InterruptedException { - // TODO listen for 404 error code when inviter supplies a non-existent JID - message.setTo(room); - - // Create the MUCUser packet that will include the invitation - MUCUser mucUser = new MUCUser(); - MUCUser.Invite invite = new MUCUser.Invite(reason, user); - mucUser.setInvite(invite); - // Add the MUCUser packet that includes the invitation to the message - message.addExtension(mucUser); - - connection.sendStanza(message); - } - /** * Invites another user to the room in which one is an occupant using a given Message. The invitation * will be sent to the room which in turn will forward the invitation to the invitee.

    @@ -1273,6 +1248,8 @@ public class MultiUserChat { return myRoomJid; } + private static final Object changeNicknameLock = new Object(); + /** * Changes the occupant's nickname to a new nickname within the room. Each room occupant * will receive two presence packets. One of type "unavailable" for the old nickname and one @@ -1287,7 +1264,7 @@ public class MultiUserChat { * @throws InterruptedException if the calling thread was interrupted. * @throws MucNotJoinedException if not joined to the Multi-User Chat. */ - public synchronized void changeNickname(Resourcepart nickname) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, MucNotJoinedException { + public void changeNickname(Resourcepart nickname) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, MucNotJoinedException { Objects.requireNonNull(nickname, "Nickname must not be null or blank."); // Check that we already have joined the room before attempting to change the // nickname. @@ -1303,18 +1280,20 @@ public class MultiUserChat { .ofType(Presence.Type.available) .build(); - // Wait for a presence packet back from the server. - StanzaFilter responseFilter = - new AndFilter( - FromMatchesFilter.createFull(jid), - new StanzaTypeFilter(Presence.class)); - StanzaCollector response = connection.createStanzaCollectorAndSend(responseFilter, joinPresence); - // Wait up to a certain number of seconds for a reply. If there is a negative reply, an - // exception will be thrown - response.nextResultOrThrow(); + synchronized (changeNicknameLock) { + // Wait for a presence packet back from the server. + StanzaFilter responseFilter = + new AndFilter( + FromMatchesFilter.createFull(jid), + new StanzaTypeFilter(Presence.class)); + StanzaCollector response = connection.createStanzaCollectorAndSend(responseFilter, joinPresence); + // Wait up to a certain number of seconds for a reply. If there is a negative reply, an + // exception will be thrown + response.nextResultOrThrow(); - // TODO: Shouldn't this handle nickname rewriting by the MUC service? - setNickname(nickname); + // TODO: Shouldn't this handle nickname rewriting by the MUC service? + setNickname(nickname); + } } /** @@ -1484,7 +1463,7 @@ public class MultiUserChat { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void banUsers(Collection jids) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { + public void banUsers(Collection jids) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { changeAffiliationByAdmin(jids, MUCAffiliation.outcast); } @@ -1504,7 +1483,7 @@ public class MultiUserChat { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void banUser(Jid jid, String reason) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { + public void banUser(BareJid jid, String reason) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { changeAffiliationByAdmin(jid, MUCAffiliation.outcast, reason); } @@ -1534,7 +1513,7 @@ public class MultiUserChat { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void grantMembership(Jid jid) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { + public void grantMembership(BareJid jid) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { changeAffiliationByAdmin(jid, MUCAffiliation.member, null); } @@ -1550,7 +1529,7 @@ public class MultiUserChat { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void revokeMembership(Collection jids) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { + public void revokeMembership(Collection jids) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { changeAffiliationByAdmin(jids, MUCAffiliation.none); } @@ -1566,14 +1545,14 @@ public class MultiUserChat { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void revokeMembership(Jid jid) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { + public void revokeMembership(BareJid jid) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { changeAffiliationByAdmin(jid, MUCAffiliation.none, null); } /** * Grants moderator privileges to participants or visitors. Room administrators may grant * moderator privileges. A moderator is allowed to kick users, grant and revoke voice, invite - * other users, modify room's subject plus all the partcipants privileges. + * other users, modify room's subject plus all the participant privileges. * * @param nicknames the nicknames of the occupants to grant moderator privileges. * @throws XMPPErrorException if an error occurs granting moderator privileges to a user. @@ -1588,7 +1567,7 @@ public class MultiUserChat { /** * Grants moderator privileges to a participant or visitor. Room administrators may grant * moderator privileges. A moderator is allowed to kick users, grant and revoke voice, invite - * other users, modify room's subject plus all the partcipants privileges. + * other users, modify room's subject plus all the participant privileges. * * @param nickname the nickname of the occupant to grant moderator privileges. * @throws XMPPErrorException if an error occurs granting moderator privileges to a user. @@ -1644,7 +1623,7 @@ public class MultiUserChat { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void grantOwnership(Collection jids) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { + public void grantOwnership(Collection jids) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { changeAffiliationByAdmin(jids, MUCAffiliation.owner); } @@ -1660,7 +1639,7 @@ public class MultiUserChat { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void grantOwnership(Jid jid) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { + public void grantOwnership(BareJid jid) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { changeAffiliationByAdmin(jid, MUCAffiliation.owner, null); } @@ -1675,7 +1654,7 @@ public class MultiUserChat { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void revokeOwnership(Collection jids) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { + public void revokeOwnership(Collection jids) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { changeAffiliationByAdmin(jids, MUCAffiliation.admin); } @@ -1690,7 +1669,7 @@ public class MultiUserChat { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void revokeOwnership(Jid jid) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { + public void revokeOwnership(BareJid jid) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { changeAffiliationByAdmin(jid, MUCAffiliation.admin, null); } @@ -1705,7 +1684,7 @@ public class MultiUserChat { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void grantAdmin(Collection jids) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { + public void grantAdmin(Collection jids) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { changeAffiliationByAdmin(jids, MUCAffiliation.admin); } @@ -1721,7 +1700,7 @@ public class MultiUserChat { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void grantAdmin(Jid jid) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { + public void grantAdmin(BareJid jid) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { changeAffiliationByAdmin(jid, MUCAffiliation.admin); } @@ -1736,7 +1715,7 @@ public class MultiUserChat { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void revokeAdmin(Collection jids) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { + public void revokeAdmin(Collection jids) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { changeAffiliationByAdmin(jids, MUCAffiliation.admin); } @@ -1752,7 +1731,7 @@ public class MultiUserChat { * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. */ - public void revokeAdmin(EntityJid jid) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { + public void revokeAdmin(BareJid jid) throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException { changeAffiliationByAdmin(jid, MUCAffiliation.member); } @@ -1863,7 +1842,7 @@ public class MultiUserChat { /** * Returns the presence info for a particular user, or null if the user - * is not in the room.

    + * is not in the room. * * @param user the room occupant to search for his presence. The format of user must * be: roomName@service/nickname (e.g. darkcave@macbeth.shakespeare.lit/thirdwitch). @@ -1877,7 +1856,7 @@ public class MultiUserChat { /** * Returns the Occupant information for a particular occupant, or null if the * user is not in the room. The Occupant object may include information such as full - * JID of the user as well as the role and affiliation of the user in the room.

    + * JID of the user as well as the role and affiliation of the user in the room. * * @param user the room occupant to search for his presence. The format of user must * be: roomName@service/nickname (e.g. darkcave@macbeth.shakespeare.lit/thirdwitch). @@ -2111,20 +2090,6 @@ public class MultiUserChat { ; } - /** - * Sends a Message to the chat room. - * - * @param message the message. - * @throws NotConnectedException if the XMPP connection is not connected. - * @throws InterruptedException if the calling thread was interrupted. - * @deprecated use {@link #sendMessage(MessageBuilder)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public void sendMessage(Message message) throws NotConnectedException, InterruptedException { - sendMessage(message.asBuilder()); - } - /** * Sends a Message to the chat room. * @@ -2146,7 +2111,7 @@ public class MultiUserChat { /** * Polls for and returns the next message, or null if there isn't * a message immediately available. This method provides significantly different - * functionalty than the {@link #nextMessage()} method since it's non-blocking. + * functionality than the {@link #nextMessage()} method since it's non-blocking. * In other words, the method call will always return immediately, whereas the * nextMessage method will return only when a message is available (or after * a specific timeout). @@ -2179,7 +2144,7 @@ public class MultiUserChat { /** * Returns the next available message in the chat. The method call will block - * (not return) until a stanza is available or the timeout has elapased. + * (not return) until a stanza is available or the timeout has elapsed. * If the timeout elapses without a result, null will be returned. * * @param timeout the maximum amount of time to wait for the next message. @@ -2243,17 +2208,20 @@ public class MultiUserChat { * @throws InterruptedException if the calling thread was interrupted. */ public void changeSubject(final String subject) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { - MessageBuilder message = buildMessage(); - message.setSubject(subject); + Message message = buildMessage() + .setSubject(subject) + .build(); // Wait for an error or confirmation message back from the server. - StanzaFilter responseFilter = new AndFilter(fromRoomGroupchatFilter, new StanzaFilter() { + StanzaFilter successFilter = new AndFilter(fromRoomGroupchatFilter, new StanzaFilter() { @Override public boolean accept(Stanza packet) { Message msg = (Message) packet; return subject.equals(msg.getSubject()); } }); - StanzaCollector response = connection.createStanzaCollectorAndSend(responseFilter, message.build()); + StanzaFilter errorFilter = new AndFilter(fromRoomFilter, new StanzaIdFilter(message), MessageTypeFilter.ERROR); + StanzaFilter responseFilter = new OrFilter(successFilter, errorFilter); + StanzaCollector response = connection.createStanzaCollectorAndSend(responseFilter, message); // Wait up to a certain number of seconds for a reply. response.nextResultOrThrow(); } @@ -2628,6 +2596,10 @@ public class MultiUserChat { for (UserStatusListener listener : userStatusListeners) { listener.membershipRevoked(); } + } else { + for (ParticipantStatusListener listener : participantStatusListeners) { + listener.membershipRevoked(from); + } } } // A occupant has changed his nickname in the room diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatException.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatException.java index 1f7f930a2..11de8dff4 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatException.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatException.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014-2015 Florian Schmaus + * Copyright © 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -88,7 +88,7 @@ public abstract class MultiUserChatException extends SmackException { /** * Thrown when trying to enter a MUC room that is not hosted a domain providing a MUC service. - * Try {@link MultiUserChatManager#getXMPPServiceDomains()} for a list of client-local domains + * Try {@link MultiUserChatManager#getMucServiceDomains()} for a list of client-local domains * providing a MUC service. */ public static class NotAMucServiceException extends MultiUserChatException { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java index 47de2163d..4d8c5c637 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014-2021 Florian Schmaus + * Copyright © 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -108,6 +108,7 @@ public final class MultiUserChatManager extends Manager { final WeakReference weakRefConnection = new WeakReference(connection); ServiceDiscoveryManager.getInstanceFor(connection).setNodeInformationProvider(DISCO_NODE, new AbstractNodeInformationProvider() { + @SuppressWarnings({"JavaUtilDate", "MixedMutabilityReturnType"}) @Override public List getNodeItems() { XMPPConnection connection = weakRefConnection.get(); @@ -148,6 +149,7 @@ public final class MultiUserChatManager extends Manager { private static final StanzaFilter DIRECT_INVITATION_FILTER = new AndFilter(StanzaTypeFilter.MESSAGE, new ExtensionElementFilter(GroupChatInvitation.class), + NotFilter.of(MUCUser.class), new NotFilter(MessageTypeFilter.ERROR)); private static final ExpirationCache KNOWN_MUC_SERVICES = new ExpirationCache<>( @@ -297,7 +299,7 @@ public final class MultiUserChatManager extends Manager { * {@link MultiUserChat#join(org.jxmpp.jid.parts.Resourcepart) join} the chat room. On some server implementations, the room will not be * created until the first person joins it. *

    - * Most XMPP servers use a sub-domain for the chat service (eg chat.example.com for the XMPP server example.com). + * Most XMPP servers use a sub-domain for the chat service (e.g.chat.example.com for the XMPP server example.com). * You must ensure that the room address you're trying to connect to includes the proper chat sub-domain. *

    * @@ -420,22 +422,6 @@ public final class MultiUserChatManager extends Manager { return serviceDiscoveryManager.findServices(MUCInitialPresence.NAMESPACE, false, false); } - /** - * Returns a collection with the XMPP addresses of the Multi-User Chat services. - * - * @return a collection with the XMPP addresses of the Multi-User Chat services. - * @throws XMPPErrorException if there was an XMPP error returned. - * @throws NoResponseException if there was no response from the remote entity. - * @throws NotConnectedException if the XMPP connection is not connected. - * @throws InterruptedException if the calling thread was interrupted. - * @deprecated use {@link #getMucServiceDomains()} instead. - */ - // TODO: Remove in Smack 4.5 - @Deprecated - public List getXMPPServiceDomains() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { - return getMucServiceDomains(); - } - /** * Check if the provided domain bare JID provides a MUC service. * @@ -480,7 +466,7 @@ public final class MultiUserChatManager extends Manager { * @throws NoResponseException if there was no response from the remote entity. * @throws NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. - * @throws NotAMucServiceException if the entity is not a MUC serivce. + * @throws NotAMucServiceException if the entity is not a MUC service. * @since 4.3.1 */ public Map getRoomsHostedBy(DomainBareJid serviceName) throws NoResponseException, XMPPErrorException, diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/ParticipantStatusListener.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/ParticipantStatusListener.java index 8a376ca36..58454d6e9 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/ParticipantStatusListener.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/ParticipantStatusListener.java @@ -129,7 +129,7 @@ public interface ParticipantStatusListener { /** * Called when an administrator grants moderator privileges to a user. This means that the user * will be able to kick users, grant and revoke voice, invite other users, modify room's - * subject plus all the partcipants privileges. + * subject plus all the participant privileges. * * @param participant the participant that was granted moderator privileges in the room * (e.g. room@conference.jabber.org/nick). @@ -140,7 +140,7 @@ public interface ParticipantStatusListener { /** * Called when an administrator revokes moderator privileges from a user. This means that the * user will no longer be able to kick users, grant and revoke voice, invite other users, - * modify room's subject plus all the partcipants privileges. + * modify room's subject plus all the participant privileges. * * @param participant the participant that was revoked moderator privileges in the room * (e.g. room@conference.jabber.org/nick). diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/RoomInfo.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/RoomInfo.java index d321903dd..b43e33ae9 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/RoomInfo.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/RoomInfo.java @@ -77,7 +77,7 @@ public class RoomInfo { */ private final boolean moderated; /** - * Every presence stanza can include the JID of every occupant unless the owner deactives this + * Every presence stanza can include the JID of every occupant unless the owner deactivates this * configuration. */ private final boolean nonanonymous; @@ -250,7 +250,7 @@ public class RoomInfo { /** * Returns the room name. *

    - * The name returnd here was provided as value of the name attribute + * The name returned here was provided as value of the name attribute * of the returned identity within the disco#info result. *

    * @@ -324,9 +324,9 @@ public class RoomInfo { } /** - * Returns true if users musy provide a valid password in order to join the room. + * Returns true if users must provide a valid password in order to join the room. * - * @return true if users musy provide a valid password in order to join the room. + * @return true if users must provide a valid password in order to join the room. */ public boolean isPasswordProtected() { return passwordProtected; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/UserStatusListener.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/UserStatusListener.java index 0e87cb5bd..93cf585fe 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/UserStatusListener.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/UserStatusListener.java @@ -28,7 +28,7 @@ import org.jxmpp.jid.Jid; * banned, or granted admin permissions or the room is destroyed. *

    * Note that the methods {@link #kicked(Jid, String)}, {@link #banned(Jid, String)} and - * {@link #roomDestroyed(MultiUserChat, String)} will be called before the generic {@link #removed(MUCUser, Presence)} + * {@link #roomDestroyed(MultiUserChat, String, String)} will be called before the generic {@link #removed(MUCUser, Presence)} * callback will be invoked. The generic {@link #removed(MUCUser, Presence)} callback will be invoked every time the user * was removed from the MUC involuntarily. It is hence the recommended callback to listen for and act upon. *

    @@ -39,7 +39,7 @@ public interface UserStatusListener { /** * Called when a moderator kicked your user from the room. This means that you are no longer - * participanting in the room. + * participating in the room. * * @param actor the moderator that kicked your user from the room (e.g. user@host.org). * @param reason the reason provided by the actor to kick you from the room. @@ -106,7 +106,7 @@ public interface UserStatusListener { /** * Called when an administrator grants moderator privileges to your user. This means that you * will be able to kick users, grant and revoke voice, invite other users, modify room's - * subject plus all the partcipants privileges. + * subject plus all the participant privileges. * */ default void moderatorGranted() { @@ -115,7 +115,7 @@ public interface UserStatusListener { /** * Called when an administrator revokes moderator privileges from your user. This means that * you will no longer be able to kick users, grant and revoke voice, invite other users, - * modify room's subject plus all the partcipants privileges. + * modify room's subject plus all the participant privileges. * */ default void moderatorRevoked() { @@ -161,10 +161,11 @@ public interface UserStatusListener { * Called when the room is destroyed. * * @param alternateMUC an alternate MultiUserChat, may be null. + * @param password a password for the alternative MultiUserChat, may be null. * @param reason the reason why the room was closed, may be null. * @see #removed(MUCUser, Presence) */ - default void roomDestroyed(MultiUserChat alternateMUC, String reason) { + default void roomDestroyed(MultiUserChat alternateMUC, String password, String reason) { } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/package-info.java index 5adaa5a6f..78866dd6a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/package-info.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/package-info.java @@ -534,7 +534,7 @@ *

    *

    Usage

    *

    - * In order to grant membership to a room, administrator privileges or owner priveliges just send + * In order to grant membership to a room, administrator privileges or owner privileges just send * **grantMembership(String jid)**, **grantAdmin(String jid)** or **grantOwnership(String jid)** to _**MultiUserChat**_ * respectively. Use **revokeMembership(String jid)**, **revokeAdmin(String jid)** or revokeOwnership(String jid)** to * revoke the membership to a room, administrator privileges or owner priveliges respectively. diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/packet/Destroy.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/packet/Destroy.java index 1361f79fe..9da151c4c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/packet/Destroy.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/packet/Destroy.java @@ -40,14 +40,22 @@ public class Destroy implements NamedElement, Serializable { private final String reason; private final EntityBareJid jid; + private final String password; public Destroy(Destroy other) { - this(other.jid, other.reason); + this(other.jid, other.password, other.reason); } public Destroy(EntityBareJid alternativeJid, String reason) { this.jid = alternativeJid; this.reason = reason; + this.password = null; + } + + public Destroy(EntityBareJid alternativeJid, String password, String reason) { + this.jid = alternativeJid; + this.password = password; + this.reason = reason; } /** @@ -59,6 +67,15 @@ public class Destroy implements NamedElement, Serializable { return jid; } + /** + * Returns the password of the alternate location. + * + * @return the password of the alternate location. + */ + public String getPassword() { + return password; + } + /** * Returns the reason for the room destruction. * @@ -73,6 +90,7 @@ public class Destroy implements NamedElement, Serializable { XmlStringBuilder xml = new XmlStringBuilder(this); xml.optAttribute("jid", getJid()); xml.rightAngleBracket(); + xml.optElement("password", getPassword()); xml.optElement("reason", getReason()); xml.closeElement(this); return xml; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/packet/MUCItem.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/packet/MUCItem.java index c1ee5ae28..8da0efb78 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/packet/MUCItem.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/packet/MUCItem.java @@ -169,8 +169,11 @@ public class MUCItem implements NamedElement { xml.optAttribute("role", getRole()); xml.rightAngleBracket(); xml.optElement("reason", getReason()); - if (getActor() != null) { - xml.halfOpenElement("actor").attribute("jid", getActor()).closeEmptyElement(); + if (getActor() != null || getActorNick() != null) { + xml.halfOpenElement("actor"); + xml.optAttribute("jid", getActor()); + xml.optAttribute("nick", getActorNick()); + xml.closeEmptyElement(); } xml.closeElement(Stanza.ITEM); return xml; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/provider/MUCParserUtils.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/provider/MUCParserUtils.java index 897fc131b..3cfa04423 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/provider/MUCParserUtils.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/provider/MUCParserUtils.java @@ -79,6 +79,7 @@ public class MUCParserUtils { final int initialDepth = parser.getDepth(); final EntityBareJid jid = ParserUtils.getBareJidAttribute(parser); String reason = null; + String password = null; outerloop: while (true) { XmlPullParser.Event eventType = parser.next(); switch (eventType) { @@ -88,6 +89,9 @@ public class MUCParserUtils { case "reason": reason = parser.nextText(); break; + case "password": + password = parser.nextText(); + break; } break; case END_ELEMENT: @@ -100,6 +104,6 @@ public class MUCParserUtils { break; } } - return new Destroy(jid, reason); + return new Destroy(jid, password, reason); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/nick/packet/Nick.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/nick/packet/Nick.java index bc1bd04b4..3ea4d3e1d 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/nick/packet/Nick.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/nick/packet/Nick.java @@ -35,7 +35,7 @@ public class Nick implements ExtensionElement { public static final QName QNAME = new QName(NAMESPACE, "nick"); /** - * Deprected, do not use. + * Deprecated, do not use. * * @deprecated use {@link #QNAME} instead. */ diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pep/PepManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pep/PepManager.java index 404da53fb..7aa6b2d0c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pep/PepManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pep/PepManager.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2015-2020 Florian Schmaus + * Copyright 2003-2007 Jive Software, 2015-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -248,32 +248,6 @@ public final class PepManager extends Manager { return pepPubSubManager; } - /** - * Adds a listener to PEPs. The listener will be fired anytime PEP events are received from remote XMPP clients. - * - * @param pepListener a roster exchange listener. - * @return true if pepListener was added. - * @deprecated use {@link #addPepEventListener(String, Class, PepEventListener)} instead. - */ - // TODO: Remove in Smack 4.5 - @Deprecated - public boolean addPepListener(PepListener pepListener) { - return pepListeners.add(pepListener); - } - - /** - * Removes a listener from PEP events. - * - * @param pepListener a roster exchange listener. - * @return true, if pepListener was removed. - * @deprecated use {@link #removePepEventListener(PepEventListener)} instead. - */ - // TODO: Remove in Smack 4.5. - @Deprecated - public boolean removePepListener(PepListener pepListener) { - return pepListeners.remove(pepListener); - } - /** * Publish an event. * @@ -288,7 +262,7 @@ public final class PepManager extends Manager { */ public LeafNode publish(String nodeId, Item item) throws NotConnectedException, InterruptedException, NoResponseException, XMPPErrorException, NotALeafNodeException { - // PEP nodes are auto created if not existent. Hence Use PubSubManager.tryToPublishAndPossibleAutoCreate() here. + // PEP nodes are auto created if not existent. Hence, use PubSubManager.tryToPublishAndPossibleAutoCreate() here. return pepPubSubManager.tryToPublishAndPossibleAutoCreate(nodeId, item); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java index b7b0f1dcd..fc39376b3 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java @@ -150,11 +150,11 @@ public final class PingManager extends Manager { // We may received an error response from an intermediate service returning an error like // 'remote-server-not-found' or 'remote-server-timeout' to us (which would fake the 'from' address, - // see RFC 6120 § 8.3.1 2.). Or the recipient could became unavailable. + // see RFC 6120 § 8.3.1 2.). Or the recipient could become unavailable. // Sticking with the current rules of RFC 6120/6121, it is undecidable at this point whether we received an // error response from the pinged entity or not. This is because a service-unavailable error condition is - // *required* (as per the RFCs) to be send back in both relevant cases: + // *required* (as per the RFCs) to be sent back in both relevant cases: // 1. When the receiving entity is unaware of the IQ request type. RFC 6120 § 8.4.: // "If an intended recipient receives an IQ stanza of type "get" or // "set" containing a child element qualified by a namespace it does @@ -244,7 +244,7 @@ public final class PingManager extends Manager { } /** - * Same as calling {@link #ping(Jid, long)} with the defaultpacket reply + * Same as calling {@link #ping(Jid, long)} with the default packet reply * timeout. * * @param jid The id of the entity the ping is being sent to diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/packet/Ping.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/packet/Ping.java index ac46a76c4..431c1c522 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/packet/Ping.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/packet/Ping.java @@ -1,6 +1,6 @@ /** * - * Copyright 2012-2019 Florian Schmaus + * Copyright 2012-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +32,7 @@ public class Ping extends SimpleIQ { super(ELEMENT, NAMESPACE); } + @SuppressWarnings("this-escape") public Ping(Jid to) { this(); setTo(to); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListManager.java index 847651f21..fa87fdd37 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/PrivacyListManager.java @@ -143,7 +143,6 @@ public final class PrivacyListManager extends Manager { else { cachedActiveListName = activeListName; } - return; } }, iqResultReplyFilter); } @@ -165,7 +164,6 @@ public final class PrivacyListManager extends Manager { else { cachedDefaultListName = defaultListName; } - return; } }, iqResultReplyFilter); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/package-info.java index 38a4c93c3..bfdc39d03 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/package-info.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/package-info.java @@ -156,7 +156,7 @@ *

    * In order to handle privacy changes, clients SHOULD listen manager’s updates. When a list is changed the manager * notifies every added listener. Listeners MUST implement the PrivacyListListener interface. Clients may - * need to react when a privacy list is modified. The PrivacyListManager lets you add listerners that will + * need to react when a privacy list is modified. The PrivacyListManager lets you add listeners that will * be notified when a list has been changed. Listeners should implement the PrivacyListListener interface. *

    *

    diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/packet/PrivacyItem.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/packet/PrivacyItem.java index c977fdaf3..a80276e5b 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/packet/PrivacyItem.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/privacy/packet/PrivacyItem.java @@ -171,7 +171,7 @@ public class PrivacyItem { } /** - * Sets wheather the receiver allows or denies incoming messages or not. + * Sets whether the receiver allows or denies incoming messages or not. * * @param filterMessage indicates if the receiver allows or denies incoming messages or not. */ diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemDeleteEvent.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemDeleteEvent.java index 424c3da0b..3ac132246 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemDeleteEvent.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemDeleteEvent.java @@ -31,7 +31,7 @@ public class ItemDeleteEvent extends SubscriptionEvent { * Constructs an ItemDeleteEvent that indicates the supplied * items (by id) have been deleted, and that the event matches the listed * subscriptions. The subscriptions would have been created by calling - * {@link LeafNode#subscribe(String)}. + * {@link LeafNode#subscribe(org.jxmpp.Jid)}. * * @param nodeId The id of the node the event came from * @param deletedItemIds The item ids of the items that were deleted. diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemPublishEvent.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemPublishEvent.java index 577dad4f7..7018b5934 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemPublishEvent.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemPublishEvent.java @@ -109,6 +109,7 @@ public class ItemPublishEvent extends SubscriptionEvent { return originalDate; } + @SuppressWarnings("JavaUtilDate") @Override public String toString() { return getClass().getName() + " [subscriptions: " + getSubscriptions() + "], [Delayed: " + diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Node.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Node.java index 4db909dfa..9741ce51f 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Node.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Node.java @@ -52,8 +52,6 @@ import org.jivesoftware.smackx.shim.packet.HeadersExtension; import org.jivesoftware.smackx.xdata.packet.DataForm; import org.jxmpp.jid.Jid; -import org.jxmpp.jid.impl.JidCreate; -import org.jxmpp.stringprep.XmppStringprepException; public abstract class Node { protected final PubSubManager pubSubManager; @@ -403,39 +401,6 @@ public abstract class Node { return reply.getExtension(PubSubElementType.SUBSCRIPTION); } - /** - * The user subscribes to the node using the supplied jid. The - * bare jid portion of this one must match the jid for the connection. - * - * Please note that the {@link Subscription.State} should be checked - * on return since more actions may be required by the caller. - * {@link Subscription.State#pending} - The owner must approve the subscription - * request before messages will be received. - * {@link Subscription.State#unconfigured} - If the {@link Subscription#isConfigRequired()} is true, - * the caller must configure the subscription before messages will be received. If it is false - * the caller can configure it but is not required to do so. - * - * @param jidString The jid to subscribe as. - * @return The subscription - * @throws XMPPErrorException if there was an XMPP error returned. - * @throws NoResponseException if there was no response from the remote entity. - * @throws NotConnectedException if the XMPP connection is not connected. - * @throws InterruptedException if the calling thread was interrupted. - * @throws IllegalArgumentException if the provided string is not a valid JID. - * @deprecated use {@link #subscribe(Jid)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public Subscription subscribe(String jidString) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { - Jid jid; - try { - jid = JidCreate.from(jidString); - } catch (XmppStringprepException e) { - throw new IllegalArgumentException(e); - } - return subscribe(jid); - } - /** * The user subscribes to the node using the supplied jid and subscription * options. The bare jid portion of this one must match the jid for the @@ -466,42 +431,6 @@ public abstract class Node { return reply.getExtension(PubSubElementType.SUBSCRIPTION); } - /** - * The user subscribes to the node using the supplied jid and subscription - * options. The bare jid portion of this one must match the jid for the - * connection. - * - * Please note that the {@link Subscription.State} should be checked - * on return since more actions may be required by the caller. - * {@link Subscription.State#pending} - The owner must approve the subscription - * request before messages will be received. - * {@link Subscription.State#unconfigured} - If the {@link Subscription#isConfigRequired()} is true, - * the caller must configure the subscription before messages will be received. If it is false - * the caller can configure it but is not required to do so. - * - * @param jidString The jid to subscribe as. - * @param subForm TODO javadoc me please - * - * @return The subscription - * @throws XMPPErrorException if there was an XMPP error returned. - * @throws NoResponseException if there was no response from the remote entity. - * @throws NotConnectedException if the XMPP connection is not connected. - * @throws InterruptedException if the calling thread was interrupted. - * @throws IllegalArgumentException if the provided string is not a valid JID. - * @deprecated use {@link #subscribe(Jid, FillableSubscribeForm)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public Subscription subscribe(String jidString, FillableSubscribeForm subForm) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { - Jid jid; - try { - jid = JidCreate.from(jidString); - } catch (XmppStringprepException e) { - throw new IllegalArgumentException(e); - } - return subscribe(jid, subForm); - } - /** * Remove the subscription related to the specified JID. This will only * work if there is only 1 subscription. If there are multiple subscriptions, diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java index cbe9c10cb..3c7c756de 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java @@ -156,33 +156,6 @@ public final class PubSubManager extends Manager { return pubSubManager; } - /** - * Deprecated. - * - * @param connection the connection. - * @return the PubSub manager for the given connection. - * @deprecated use {@link #getInstanceFor(XMPPConnection)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public static PubSubManager getInstance(XMPPConnection connection) { - return getInstanceFor(connection); - } - - /** - * Deprecated. - * - * @param connection the connection. - * @param pubSubService the XMPP address of the PubSub service. - * @return the PubSub manager for the given connection. - * @deprecated use {@link #getInstanceFor(XMPPConnection, BareJid)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public static PubSubManager getInstance(XMPPConnection connection, BareJid pubSubService) { - return getInstanceFor(connection, pubSubService); - } - /** * Create a pubsub manager associated to the specified connection where * the pubsub requests require a specific to address for packets. @@ -263,7 +236,7 @@ public final class PubSubManager extends Manager { DataForm submitForm = config.getDataFormToSubmit(); request.addExtension(new FormNode(FormNodeType.CONFIGURE, submitForm)); NodeType nodeType = config.getNodeType(); - // Note that some implementations do to have the pubsub#node_type field in their defauilt configuration, + // Note that some implementations do to have the pubsub#node_type field in their default configuration, // which I believe to be a bug. However, since PubSub specifies the default node type to be 'leaf' we assume // leaf if the field does not exist. isLeafNode = nodeType == null || nodeType == NodeType.leaf; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/SimplePayload.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/SimplePayload.java index 200be7f4b..95667a6e5 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/SimplePayload.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/SimplePayload.java @@ -59,27 +59,6 @@ public class SimplePayload implements XmlElement { ns = StringUtils.requireNotNullNorEmpty(qname.getNamespaceURI(), "Could not determine namespace from XML payload"); } - /** - * Construct a SimplePayload object with the specified element name, - * namespace and content. The content must be well formed XML. - * - * @param elementName The root element name (of the payload) - * @param namespace The namespace of the payload, null if there is none - * @param xmlPayload The payload data - * @deprecated use {@link #SimplePayload(String)} insteas. - */ - // TODO: Remove in Smack 4.5 - @Deprecated - public SimplePayload(String elementName, String namespace, CharSequence xmlPayload) { - this(xmlPayload.toString()); - if (!elementName.equals(this.elemName)) { - throw new IllegalArgumentException(); - } - if (!namespace.equals(this.ns)) { - throw new IllegalArgumentException(); - } - } - @Override public String getElementName() { return elemName; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/PubSub.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/PubSub.java index a2756d5ff..138558207 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/PubSub.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/packet/PubSub.java @@ -43,13 +43,14 @@ public class PubSub extends IQ { super(ELEMENT, ns.getXmlns()); } + @SuppressWarnings("this-escape") public PubSub(Jid to, Type type, PubSubNamespace ns) { super(ELEMENT, (ns == null ? PubSubNamespace.basic : ns).getXmlns()); setTo(to); setType(type); } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) public PE getExtension(PubSubElementType elem) { return (PE) getExtensionElement(elem.getElementName(), elem.getNamespace().getXmlns()); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/rsm/RSMManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/rsm/RSMManager.java index ea6924fae..9f03c257a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/rsm/RSMManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/rsm/RSMManager.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014-2021 Florian Schmaus + * Copyright © 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,8 @@ */ package org.jivesoftware.smackx.rsm; +import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedList; import java.util.List; import org.jivesoftware.smack.packet.XmlElement; @@ -29,7 +29,7 @@ import org.jivesoftware.smackx.rsm.packet.RSMSet.PageDirection; public class RSMManager { Collection page(int max) { - List packetExtensions = new LinkedList<>(); + List packetExtensions = new ArrayList<>(); packetExtensions.add(new RSMSet(max)); return packetExtensions; } @@ -45,7 +45,7 @@ public class RSMManager { throw new IllegalArgumentException("returnedExtensions must no be null"); } if (additionalExtensions == null) { - additionalExtensions = new LinkedList<>(); + additionalExtensions = new ArrayList<>(); } RSMSet resultRsmSet = PacketUtil.extensionElementFrom(returnedExtensions, RSMSet.ELEMENT, RSMSet.NAMESPACE); if (resultRsmSet == null) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/search/ReportedData.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/search/ReportedData.java index 36853164b..ac2cec39d 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/search/ReportedData.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/search/ReportedData.java @@ -150,7 +150,7 @@ public class ReportedData { /** * Creates a new column with the specified definition. * - * @param label the columns's label. + * @param label the column's label. * @param variable the variable name of the column. * @param type the format for the returned data. */ diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/si/provider/StreamInitiationProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/si/provider/StreamInitiationProvider.java index a4be338b5..d6333ed80 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/si/provider/StreamInitiationProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/si/provider/StreamInitiationProvider.java @@ -45,6 +45,7 @@ import org.jxmpp.util.XmppDateTime; public class StreamInitiationProvider extends IqProvider { private static final Logger LOGGER = Logger.getLogger(StreamInitiationProvider.class.getName()); + @SuppressWarnings("JavaUtilDate") @Override public StreamInitiation parse(XmlPullParser parser, int initialDepth, IqData iqData, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException { // si diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/Time.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/Time.java index bb7632210..387a7d390 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/Time.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/Time.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2014-2021 Florian Schmaus + * Copyright 2003-2007 Jive Software, 2014-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,6 +46,7 @@ public class Time extends IQ implements TimeView { private final String utc; private final String tzo; + @SuppressWarnings("this-escape") public Time(TimeBuilder timeBuilder) { super(timeBuilder, ELEMENT, NAMESPACE); utc = timeBuilder.getUtc(); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/vcardtemp/VCardManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/vcardtemp/VCardManager.java index 0bfbba9b2..5aefdeeff 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/vcardtemp/VCardManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/vcardtemp/VCardManager.java @@ -106,7 +106,7 @@ public final class VCardManager extends Manager { vcard.setType(IQ.Type.set); // Also make sure to generate a new stanza id (the given vcard could be a vcard result), in which case we don't // want to use the same stanza id again (although it wouldn't break if we did) - vcard.setStanzaId(); + // vcard.setStanzaId(); TODO FIXME connection().sendIqRequestAndWaitForResponse(vcard); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/vcardtemp/packet/VCard.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/vcardtemp/packet/VCard.java index 4f4d74a19..4d6f18ecf 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/vcardtemp/packet/VCard.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/vcardtemp/packet/VCard.java @@ -122,7 +122,7 @@ public final class VCard extends IQ { private String photoBinval; /** - * Such as DESC ROLE GEO etc.. see XEP-0054 + * Such as DESC ROLE GEO etc. see XEP-0054 */ private final Map otherSimpleFields = new HashMap<>(); @@ -575,7 +575,7 @@ public final class VCard extends IQ { * Load VCard information for a given user. XMPPConnection should be authenticated and not anonymous. * * @param connection connection. - * @param user user whos information we want to load. + * @param user user whose information we want to load. * * @throws XMPPErrorException if there was an XMPP error returned. * @throws NoResponseException if there was no response from the server. diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java index 6681e6408..756c14b52 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/BooleanFormField.java @@ -36,7 +36,7 @@ public class BooleanFormField extends SingleValueFormField { } /** - * Get the value of the booelan field. Note that, if no explicit boolean value is provided, in the form of "true", + * Get the value of the boolean field. Note that, if no explicit boolean value is provided, in the form of "true", * "false", "0", or "1", then the default value of a boolean field is false, according to * XEP-0004 § 3.3. * diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java index a2ad8cb76..bd172be54 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2019-2021 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2019-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -342,23 +342,6 @@ public abstract class FormField implements XmlElement { return XmppDateTime.parseXEP0082Date(valueString); } - /** - * Returns the field's name, also known as the variable name in case this is an filled out answer form. - *

    - * According to XEP-4 § 3.2 the variable name (the 'var' attribute) - * "uniquely identifies the field in the context of the form" (if the field is not of type 'fixed', in which case - * the field "MAY possess a 'var' attribute") - *

    - * - * @return the field's name. - * @deprecated use {@link #getFieldName()} instead. - */ - // TODO: Remove in Smack 4.5 - @Deprecated - public String getVariable() { - return getFieldName(); - } - /** * Returns the field's name, also known as the variable name in case this is an filled out answer form. *

    @@ -403,7 +386,7 @@ public abstract class FormField implements XmlElement { protected transient List extraXmlChildElements; /** - * Populate @{link {@link #extraXmlChildElements}}. Note that this method may be overridden by subclasses. + * Populate {@link #extraXmlChildElements}. Note that this method may be overridden by subclasses. */ protected void populateExtraXmlChildElements() { List values = getRawValues(); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FillableForm.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FillableForm.java index c14990a9a..302156c2c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FillableForm.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FillableForm.java @@ -1,6 +1,6 @@ /** * - * Copyright 2020 Florian Schmaus + * Copyright 2020-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,8 @@ import org.jivesoftware.smackx.xdata.AbstractMultiFormField; import org.jivesoftware.smackx.xdata.AbstractSingleStringValueFormField; import org.jivesoftware.smackx.xdata.FormField; import org.jivesoftware.smackx.xdata.FormFieldChildElement; +import org.jivesoftware.smackx.xdata.ListMultiFormField; +import org.jivesoftware.smackx.xdata.ListSingleFormField; import org.jivesoftware.smackx.xdata.packet.DataForm; import org.jxmpp.jid.Jid; @@ -47,6 +49,7 @@ public class FillableForm extends FilledForm { private final Map filledFields = new HashMap<>(); + @SuppressWarnings("this-escape") public FillableForm(DataForm dataForm) { super(dataForm); if (dataForm.getType() != DataForm.Type.form) { @@ -54,6 +57,7 @@ public class FillableForm extends FilledForm { } Set requiredFields = new HashSet<>(); + List requiredFieldsWithDefaultValue = new ArrayList<>(); for (FormField formField : dataForm.getFields()) { if (formField.isRequired()) { String fieldName = formField.getFieldName(); @@ -61,13 +65,17 @@ public class FillableForm extends FilledForm { if (formField.hasValueSet()) { // This is a form field with a default value. - write(formField); + requiredFieldsWithDefaultValue.add(formField); } else { missingRequiredFields.add(fieldName); } } } this.requiredFields = Collections.unmodifiableSet(requiredFields); + + for (FormField field : requiredFieldsWithDefaultValue) { + write(field); + } } protected void writeListMulti(String fieldName, List values) { @@ -221,11 +229,20 @@ public class FillableForm extends FilledForm { if (filledFormField.getType() == FormField.Type.fixed) { throw new IllegalArgumentException(); } - if (!filledFormField.hasValueSet()) { - throw new IllegalArgumentException(); - } String fieldName = filledFormField.getFieldName(); + + boolean isListField = filledFormField instanceof ListMultiFormField + || filledFormField instanceof ListSingleFormField; + // Only list-* fields require a value to be set. Other fields types can be empty. For example MUC's + // muc#roomconfig_roomadmins, which is of type jid-multi, is submitted without values to reset the room's admin + // list. + if (isListField && !filledFormField.hasValueSet()) { + throw new IllegalArgumentException("Tried to write form field " + fieldName + " of type " + + filledFormField.getType() + + " without any values set. However, according to XEP-0045 § 3.3 fields of type list-multi or list-single must have one item set."); + } + if (!getDataForm().hasField(fieldName)) { throw new IllegalArgumentException(); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/packet/DataForm.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/packet/DataForm.java index 9ab354d57..e9f2da625 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/packet/DataForm.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/packet/DataForm.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2020-2021 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2020-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,6 @@ import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.StanzaView; import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.util.CollectionUtil; -import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.XmlStringBuilder; @@ -356,8 +355,7 @@ public final class DataForm implements ExtensionElement { } public static final class Builder { - // TODO: Make this field final once setType() is gone. - private Type type; + private final Type type; private String title; private List instructions; private ReportedData reportedData; @@ -409,20 +407,6 @@ public final class DataForm implements ExtensionElement { } } - /** - * Deprecated do not use. - * - * @param type the type. - * @return a reference to this builder. - * @deprecated use {@link DataForm#builder(Type)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5 and then make this.type final. - public Builder setType(Type type) { - this.type = Objects.requireNonNull(type); - return this; - } - /** * Sets the description of the data. It is similar to the title on a web page or an X window. * You can put a <title/> on either a form to fill out, or a set of data results. diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/package-info.java index 7054e0f22..7ebb8cee9 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/package-info.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/package-info.java @@ -152,7 +152,7 @@ * Before you start to send XHTML messages to a user you should discover if the user supports XHTML messages. There are * two ways to achieve the discovery, explicitly and implicitly. Explicit is when you first try to discover if the user * supports XHTML before sending any XHTML message. Implicit is when you send XHTML messages without first discovering - * if the conversation partner's client supports XHTML and depenging on the answer (normal message or XHTML message) you + * if the conversation partner's client supports XHTML and depending on the answer (normal message or XHTML message) you * find out if the user supports XHTML messages or not. This section explains how to explicitly discover for XHTML * support. *

    diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamManagerTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamManagerTest.java index 00e721e5b..0c59cf73c 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamManagerTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamManagerTest.java @@ -179,7 +179,7 @@ public class Socks5ByteStreamManagerTest { Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); byteStreamManager.setAnnounceLocalStreamHost(false); - /** + /* * create responses in the order they should be queried specified by the XEP-0065 * specification */ @@ -231,7 +231,7 @@ public class Socks5ByteStreamManagerTest { Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); byteStreamManager.setAnnounceLocalStreamHost(false); - /** + /* * create responses in the order they should be queried specified by the XEP-0065 * specification */ @@ -292,7 +292,7 @@ public class Socks5ByteStreamManagerTest { Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); byteStreamManager.setAnnounceLocalStreamHost(false); - /** + /* * create responses in the order they should be queried specified by the XEP-0065 * specification */ @@ -375,7 +375,7 @@ public class Socks5ByteStreamManagerTest { Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); byteStreamManager.setAnnounceLocalStreamHost(false); - /** + /* * create responses in the order they should be queried specified by the XEP-0065 * specification */ @@ -454,7 +454,7 @@ public class Socks5ByteStreamManagerTest { // TODO: It appears that it is not required to disable the local stream host for this unit test. byteStreamManager.setAnnounceLocalStreamHost(false); - /** + /* * create responses in the order they should be queried specified by the XEP-0065 * specification */ @@ -541,7 +541,7 @@ public class Socks5ByteStreamManagerTest { byteStreamManager.setAnnounceLocalStreamHost(false); byteStreamManager.setProxyConnectionTimeout(3000); - /** + /* * create responses in the order they should be queried specified by the XEP-0065 * specification */ @@ -634,7 +634,7 @@ public class Socks5ByteStreamManagerTest { Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); byteStreamManager.setAnnounceLocalStreamHost(false); - /** + /* * create responses in the order they should be queried specified by the XEP-0065 * specification */ @@ -760,7 +760,7 @@ public class Socks5ByteStreamManagerTest { // get Socks5ByteStreamManager for connection Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - /** + /* * create responses in the order they should be queried specified by the XEP-0065 * specification */ diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/caps/EntityCapsManagerTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/caps/EntityCapsManagerTest.java index 87700708e..d9a0666c3 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/caps/EntityCapsManagerTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/caps/EntityCapsManagerTest.java @@ -22,8 +22,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedList; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.test.util.SmackTestSuite; @@ -47,6 +47,41 @@ import org.jxmpp.stringprep.XmppStringprepException; public class EntityCapsManagerTest extends SmackTestSuite { + /** + * XEP- + * 0115 Simple Generation Example. + * @throws XmppStringprepException if the provided string is invalid. + */ + @Test + public void testSimpleGenerationExample() throws XmppStringprepException { + DiscoverInfo di = createSimpleSamplePacket(); + + CapsVersionAndHash versionAndHash = EntityCapsManager.generateVerificationString(di, StringUtils.SHA1); + assertEquals("QgayPKawpkPSDYmwT/WM94uAlu0=", versionAndHash.version); + } + + /** + * Asserts that the order in which data forms are present in the disco/info does not affect the calculated + * verification string, as the XEP mandates that these are ordered by FORM_TYPE (i.e., by the XML character data of + * the element). + * @throws XmppStringprepException if the provided string is invalid. + */ + @Test + public void testReversedDataFormOrder() throws XmppStringprepException { + final DiscoverInfoBuilder builderA = createSimpleSampleBuilder(); + builderA.addExtension(createSampleServerInfoDataForm()); // This works, as the underlying MultiMap maintains insertion-order. + builderA.addExtension(createSampleSoftwareInfoDataForm()); + + final DiscoverInfoBuilder builderB = createSimpleSampleBuilder(); + builderB.addExtension(createSampleSoftwareInfoDataForm()); + builderB.addExtension(createSampleServerInfoDataForm()); + + CapsVersionAndHash versionAndHashA = EntityCapsManager.generateVerificationString(builderA.build(), StringUtils.SHA1); + CapsVersionAndHash versionAndHashB = EntityCapsManager.generateVerificationString(builderB.build(), StringUtils.SHA1); + + assertEquals(versionAndHashA.version, versionAndHashB.version); + } + /** * XEP- * 0115 Complex Generation Example. @@ -142,13 +177,48 @@ public class EntityCapsManagerTest extends SmackTestSuite { return df.build(); } + private static DataForm createSampleServerInfoDataForm() { + DataForm.Builder df = DataForm.builder(DataForm.Type.result); + + { + TextMultiFormField.Builder ff = FormField.textMultiBuilder("admin-addresses"); + ff.addValue("xmpp:admin@example.org"); + ff.addValue("mailto:admin@example.com"); + df.addField(ff.build()); + } + + { + TextSingleFormField.Builder ff = FormField.hiddenBuilder("FORM_TYPE"); + ff.setValue("http://jabber.org/network/serverinfo"); + df.addField(ff.build()); + } + + return df.build(); + } + + private static DiscoverInfoBuilder createSimpleSampleBuilder() throws XmppStringprepException { + DiscoverInfoBuilder di = DiscoverInfo.builder("disco1"); + di.ofType(IQ.Type.result); + + di.addIdentity(new DiscoverInfo.Identity("client", "Exodus 0.9.1", "pc")); + di.addFeature("http://jabber.org/protocol/disco#info"); + di.addFeature("http://jabber.org/protocol/disco#items"); + di.addFeature("http://jabber.org/protocol/muc"); + di.addFeature("http://jabber.org/protocol/caps"); + + return di; + } + private static DiscoverInfo createSimpleSamplePacket() throws XmppStringprepException { + return createSimpleSampleBuilder().build(); + } + private static DiscoverInfo createComplexSamplePacket() throws XmppStringprepException { DiscoverInfoBuilder di = DiscoverInfo.builder("disco1"); di.from(JidCreate.from("benvolio@capulet.lit/230193")); di.to(JidCreate.from("juliet@capulet.lit/chamber")); di.ofType(IQ.Type.result); - Collection identities = new LinkedList(); + Collection identities = new ArrayList(); DiscoverInfo.Identity i = new DiscoverInfo.Identity("client", "pc", "Psi 0.11", "en"); identities.add(i); i = new DiscoverInfo.Identity("client", "pc", "Ψ 0.11", "el"); @@ -171,7 +241,7 @@ public class EntityCapsManagerTest extends SmackTestSuite { di.to(")juliet@capulet.lit/chamber"); di.ofType(IQ.Type.result); - Collection identities = new LinkedList(); + Collection identities = new ArrayList(); DiscoverInfo.Identity i = new DiscoverInfo.Identity("client", "pc", "Psi 0.11", "en"); identities.add(i); i = new DiscoverInfo.Identity("client", "pc", "Ψ 0.11", "el"); diff --git a/smack-extensions/src/testFixtures/java/org/jivesoftware/util/Protocol.java b/smack-extensions/src/testFixtures/java/org/jivesoftware/util/Protocol.java index f256a9728..9e1bdc652 100644 --- a/smack-extensions/src/testFixtures/java/org/jivesoftware/util/Protocol.java +++ b/smack-extensions/src/testFixtures/java/org/jivesoftware/util/Protocol.java @@ -89,6 +89,7 @@ public class Protocol { public boolean printProtocol = false; // responses to requests are taken form this queue + @SuppressWarnings("JdkObsolete") private final Queue responses = new LinkedList<>(); // list of verifications diff --git a/smack-im/build.gradle b/smack-im/build.gradle index f3cb2d475..beb30a966 100644 --- a/smack-im/build.gradle +++ b/smack-im/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} + description = """\ Smack IM. Classes and methods for XMPP-IM (RFC 6121): @@ -8,4 +13,7 @@ Roster, Chat and other functionality.""" dependencies { api project(':smack-core') testImplementation(testFixtures(project(":smack-core"))) + + // TODO: Migrate Junit4 tests to Junit5. + testImplementation "org.junit.vintage:junit-vintage-engine:$junitVersion" } diff --git a/smack-im/src/main/java/org/jivesoftware/smack/chat/Chat.java b/smack-im/src/main/java/org/jivesoftware/smack/chat/Chat.java index c57bc5e34..a9cbe98b1 100644 --- a/smack-im/src/main/java/org/jivesoftware/smack/chat/Chat.java +++ b/smack-im/src/main/java/org/jivesoftware/smack/chat/Chat.java @@ -79,7 +79,7 @@ public class Chat { /** * Returns the name of the user the chat is with. * - * @return the name of the user the chat is occuring with. + * @return the name of the user the chat is occurring with. */ public EntityJid getParticipant() { return participant; @@ -133,10 +133,12 @@ public class Chat { public void sendMessage(Message message) throws NotConnectedException, InterruptedException { // Force the recipient, message type, and thread ID since the user elected // to send the message through this chat object. - message.setTo(participant); - message.setType(Message.Type.chat); - message.setThread(threadID); - chatManager.sendMessage(this, message); + Message chatMessage = message.asBuilder() + .to(participant) + .ofType(Message.Type.chat) + .setThread(threadID) + .build(); + chatManager.sendMessage(this, chatMessage); } /** @@ -199,10 +201,10 @@ public class Chat { // Because the collector and listeners are expecting a thread ID with // a specific value, set the thread ID on the message even though it // probably never had one. - message.setThread(threadID); + Message chatMessage = message.asBuilder().setThread(threadID).build(); for (ChatMessageListener listener : listeners) { - listener.processMessage(this, message); + listener.processMessage(this, chatMessage); } } diff --git a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java index 8df18a77b..b51542015 100644 --- a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java +++ b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2016-2022 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2016-2024 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -118,8 +118,8 @@ import org.jxmpp.util.cache.LruCache; * Every entry in the roster has presence associated with it. The * {@link #getPresence(BareJid)} method will return a Presence object with the * user's presence or `null` if the user is not online or you are not subscribed - * to the user's presence. _Note:_ Presence subscription is nnot tied to the - * user being on the roster, and vice versa: You could be subscriped to a remote + * to the user's presence. _Note:_ Presence subscription is not tied to the + * user being on the roster, and vice versa: You could be subscribed to a remote * users presence without the user in your roster, and a remote user can be in * your roster without any presence subscription relation. *

    @@ -218,7 +218,7 @@ public final class Roster extends Manager { *

    * This method will never return null, instead if the user has not yet logged into * the server all modifying methods of the returned roster object - * like {@link Roster#createEntry(BareJid, String, String[])}, + * like {@link Roster#createItemAndRequestSubscription(BareJid, String, String[])}, * {@link Roster#removeEntry(RosterEntry)} , etc. except adding or removing * {@link RosterListener}s will throw an IllegalStateException. *

    @@ -242,13 +242,13 @@ public final class Roster extends Manager { private static boolean rosterLoadedAtLoginDefault = true; /** - * The default subscription processing mode to use when a Roster is created. By default + * The default subscription processing mode to use when a Roster is created. By default, * all subscription requests are automatically rejected. */ private static SubscriptionMode defaultSubscriptionMode = SubscriptionMode.reject_all; /** - * The initial maximum size of the map holding presence information of entities without an Roster entry. Currently + * The initial maximum size of the map holding presence information of entities without a Roster entry. Currently * {@value #INITIAL_DEFAULT_NON_ROSTER_PRESENCE_MAP_SIZE}. */ public static final int INITIAL_DEFAULT_NON_ROSTER_PRESENCE_MAP_SIZE = 1024; @@ -285,7 +285,7 @@ public final class Roster extends Manager { private final Map> presenceMap = new ConcurrentHashMap<>(); /** - * Like {@link presenceMap} but for presences of entities not in our Roster. + * Like {@link #presenceMap} but for presences of entities not in our Roster. */ // TODO Ideally we want here to use a LRU cache like Map which will evict all superfluous items // if their maximum size is lowered below the current item count. LruCache does not provide @@ -299,7 +299,7 @@ public final class Roster extends Manager { private final Set rosterLoadedListeners = new LinkedHashSet<>(); /** - * Mutually exclude roster listener invocation and changing the {@link entries} map. Also used + * Mutually exclude roster listener invocation and changing the {@link #entries} map. Also used * to synchronize access to either the roster listeners or the entries map. */ private final Object rosterListenersAndEntriesLock = new Object(); @@ -439,7 +439,7 @@ public final class Roster extends Manager { return; } - // Ensure that all available presences received so far in a eventually existing previous session are + // Ensure that all available presences received so far in an eventually existing previous session are // marked 'offline'. setOfflinePresencesAndResetLoaded(); @@ -754,27 +754,6 @@ public final class Roster extends Manager { return group; } - /** - * Creates a new roster entry and presence subscription. The server will asynchronously - * update the roster with the subscription status. - * - * @param user the user. (e.g. johndoe@jabber.org) - * @param name the nickname of the user. - * @param groups the list of group names the entry will belong to, or null if the - * the roster entry won't belong to a group. - * @throws NoResponseException if there was no response from the server. - * @throws XMPPErrorException if an XMPP exception occurs. - * @throws NotLoggedInException If not logged in. - * @throws NotConnectedException if the XMPP connection is not connected. - * @throws InterruptedException if the calling thread was interrupted. - * @deprecated use {@link #createItemAndRequestSubscription(BareJid, String, String[])} instead. - */ - // TODO: Remove in Smack 4.5. - @Deprecated - public void createEntry(BareJid user, String name, String[] groups) throws NotLoggedInException, NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { - createItemAndRequestSubscription(user, name, groups); - } - /** * Creates a new roster item. The server will asynchronously update the roster with the subscription status. *

    @@ -818,7 +797,7 @@ public final class Roster extends Manager { * * @param jid the XMPP address of the contact (e.g. johndoe@jabber.org) * @param name the nickname of the user. - * @param groups the list of group names the entry will belong to, or null if the + * @param groups the list of group names the entry will belong to, or null if * the roster entry won't belong to a group. * @throws NoResponseException if there was no response from the server. * @throws XMPPErrorException if an XMPP exception occurs. @@ -839,7 +818,7 @@ public final class Roster extends Manager { * * @param user the user. (e.g. johndoe@jabber.org) * @param name the nickname of the user. - * @param groups the list of group names the entry will belong to, or null if the + * @param groups the list of group names the entry will belong to, or null if * the roster entry won't belong to a group. * @throws NoResponseException if there was no response from the server. * @throws XMPPErrorException if an XMPP exception occurs. @@ -1042,7 +1021,7 @@ public final class Roster extends Manager { * Returns the roster entry associated with the given XMPP address or * null if the user is not an entry in the roster. * - * @param jid the XMPP address of the user (eg "jsmith@example.com"). The address could be + * @param jid the XMPP address of the user (e.g."jsmith@example.com"). The address could be * in any valid format (e.g. "domain/resource", "user@domain" or "user@domain/resource"). * @return the roster entry or null if it does not exist. */ @@ -1056,7 +1035,7 @@ public final class Roster extends Manager { /** * Returns true if the specified XMPP address is an entry in the roster. * - * @param jid the XMPP address of the user (eg "jsmith@example.com"). The + * @param jid the XMPP address of the user (e.g."jsmith@example.com"). The * address must be a bare JID e.g. "domain/resource" or * "user@domain". * @return true if the XMPP address is an entry in the roster. @@ -1118,11 +1097,11 @@ public final class Roster extends Manager { * {@link RosterListener}. *

    * - * @param jid the XMPP address of the user (eg "jsmith@example.com"). The + * @param jid the XMPP address of the user (e.g."jsmith@example.com"). The * address must be a bare JID e.g. "domain/resource" or * "user@domain". * @return the user's current presence, or unavailable presence if the user is offline - * or if no presence information is available.. + * or if no presence information is available. */ public Presence getPresence(BareJid jid) { Map userPresences = getPresencesInternal(jid); @@ -1652,11 +1631,20 @@ public final class Roster extends Manager { } } + + final Jid from = packet.getFrom(); + if (!isLoaded() && rosterLoadedAtLogin) { - LOGGER.warning("Roster not loaded while processing " + packet); + XMPPConnection connection = connection(); + + // Only log the warning, if this is not the reflected self-presence. Otherwise, + // the reflected self-presence may cause a spurious warning in case the + // connection got quickly shut down. See SMACK-941. + if (connection != null && from != null && !from.equals(connection.getUser())) { + LOGGER.warning("Roster not loaded while processing " + packet); + } } final Presence presence = (Presence) packet; - final Jid from = presence.getFrom(); final BareJid key; if (from != null) { diff --git a/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterEntry.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterEntry.java index 8a13fda90..0360f892b 100644 --- a/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterEntry.java +++ b/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterEntry.java @@ -108,7 +108,7 @@ public final class RosterEntry extends Manager { packet.setType(IQ.Type.set); // Create a new roster item with the current RosterEntry and the *new* name. Note that we can't set the name of - // RosterEntry right away, as otherwise the updated event wont get fired, because equalsDeep would return true. + // RosterEntry right away, as otherwise the updated event won't get fired, because equalsDeep would return true. packet.addRosterItem(toRosterItem(this, name)); connection().sendIqRequestAndWaitForResponse(packet); @@ -136,7 +136,7 @@ public final class RosterEntry extends Manager { } /** - * Returns an copied list of the roster groups that this entry belongs to. + * Returns a copied list of the roster groups that this entry belongs to. * * @return an iterator for the groups this entry belongs to. */ @@ -306,7 +306,7 @@ public final class RosterEntry extends Manager { * * @param entry the roster entry. * @param name the name of the roster item. - * @param includeAskAttribute whether or not to include the 'ask' attribute. + * @param includeAskAttribute whether to include the 'ask' attribute. * @return the roster item. */ private static RosterPacket.Item toRosterItem(RosterEntry entry, String name, boolean includeAskAttribute) { diff --git a/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterGroup.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterGroup.java index f8718fcbb..d6467dd83 100644 --- a/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterGroup.java +++ b/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterGroup.java @@ -116,7 +116,7 @@ public class RosterGroup extends Manager { * Returns the roster entry associated with the given XMPP address or * null if the user is not an entry in the group. * - * @param user the XMPP address of the user (eg "jsmith@example.com"). + * @param user the XMPP address of the user (e.g."jsmith@example.com"). * @return the roster entry or null if it does not exist in the group. */ public RosterEntry getEntry(Jid user) { diff --git a/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterUtil.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterUtil.java index 843b24bad..5f2093404 100644 --- a/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterUtil.java +++ b/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterUtil.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016 Florian Schmaus + * Copyright 2016-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ import org.jxmpp.jid.Jid; public class RosterUtil { + @SuppressWarnings("JavaUtilDate") public static void waitUntilOtherEntityIsSubscribed(Roster roster, BareJid otherEntity, long timeoutMillis) throws InterruptedException, TimeoutException { Date deadline = new Date(System.currentTimeMillis() + timeoutMillis); @@ -147,6 +148,7 @@ public class RosterUtil { ensureSubscribedTo(connectionTwo, connectionOne, timeout); } + @SuppressWarnings("JavaUtilDate") public static void ensureSubscribedTo(XMPPConnection connectionOne, XMPPConnection connectionTwo, long timeout) throws NotLoggedInException, NotConnectedException, InterruptedException, TimeoutException { Date deadline = new Date(System.currentTimeMillis() + timeout); diff --git a/smack-integration-test/build.gradle b/smack-integration-test/build.gradle index ea08655af..ede353266 100644 --- a/smack-integration-test/build.gradle +++ b/smack-integration-test/build.gradle @@ -1,13 +1,18 @@ -apply plugin: 'application' +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.application-conventions' +} description = """\ Smack integration tests.""" -mainClassName = 'org.igniterealtime.smack.inttest.SmackIntegrationTestFramework' -applicationDefaultJvmArgs = ["-enableassertions"] + +application { + mainClass = 'org.igniterealtime.smack.inttest.SmackIntegrationTestFramework' +} dependencies { - api project(':smack-java8-full') + api project(':smack-java11-full') api project(':smack-resolver-dnsjava') implementation project(':smack-websocket-java11') implementation "com.google.guava:guava:${guavaVersion}" @@ -20,8 +25,3 @@ dependencies { testFixturesApi(testFixtures(project(":smack-core"))) testImplementation "org.jxmpp:jxmpp-jid:$jxmppVersion:tests" } - -run { - // Pass all system properties down to the "application" run - systemProperties System.getProperties() -} diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/XmppConnectionStressTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/XmppConnectionStressTest.java index 8afe9f9e4..a0f658977 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/XmppConnectionStressTest.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/XmppConnectionStressTest.java @@ -100,8 +100,8 @@ public class XmppConnectionStressTest { for (int c = 0; c < payloadChunkCount; c++) { int payloadChunkSize = random.nextInt(configuration.maxPayloadChunkSize) + 1; - String payloadCunk = StringUtils.randomString(payloadChunkSize, random); - JivePropertiesManager.addProperty(messageBuilder, "payload-chunk-" + c, payloadCunk); + String payloadChunk = StringUtils.randomString(payloadChunkSize, random); + JivePropertiesManager.addProperty(messageBuilder, "payload-chunk-" + c, payloadChunk); } JivePropertiesManager.addProperty(messageBuilder, MESSAGE_NUMBER_PROPERTY, i); @@ -184,7 +184,7 @@ public class XmppConnectionStressTest { Exception exception = new Exception(exceptionMessage.toString()); receiveExceptions.put(connection, exception); // TODO: Current Smack design does not guarantee that the listener won't be invoked again. - // This is because the decission to invoke a sync listeners is done at a different place + // This is because the decision to invoke a sync listeners is done at a different place // then invoking the listener. connection.removeSyncStanzaListener(this); receivedSemaphore.release(); @@ -338,7 +338,7 @@ public class XmppConnectionStressTest { sb.append("Exceptions while sending and/or receiving."); if (!sendExceptions.isEmpty()) { - sb.append(" Send exxceptions: "); + sb.append(" Send exceptions: "); for (Map.Entry entry : sendExceptions.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append(';'); } diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java index 2b94b2190..9f94fe26b 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2020 Florian Schmaus + * Copyright 2015-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; +import java.util.List; import java.util.Random; import java.util.concurrent.TimeoutException; import java.util.logging.Logger; @@ -33,6 +34,11 @@ import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.filter.StanzaFilter; +import org.igniterealtime.smack.inttest.util.MultiResultSyncPoint; +import org.igniterealtime.smack.inttest.util.ResultSyncPoint; + +import org.opentest4j.AssertionFailedError; + public abstract class AbstractSmackIntTest { protected static final Logger LOGGER = Logger.getLogger(AbstractSmackIntTest.class.getName()); @@ -90,4 +96,32 @@ public abstract class AbstractSmackIntTest { } return urlConnection; } + + public R assertResult(ResultSyncPoint syncPoint, String message) throws InterruptedException, TimeoutException, AssertionFailedError { + return assertResult(syncPoint, timeout, message); + } + + public static R assertResult(ResultSyncPoint syncPoint, long timeout, String message) throws InterruptedException, TimeoutException, AssertionFailedError { + try { + return syncPoint.waitForResult(timeout, message); + } catch (InterruptedException | TimeoutException e) { + throw e; + } catch (Exception e) { + throw new AssertionFailedError(message, e); + } + } + + public List assertResult(MultiResultSyncPoint syncPoint, String message) throws InterruptedException, TimeoutException, AssertionFailedError { + return assertResult(syncPoint, timeout, message); + } + + public static List assertResult(MultiResultSyncPoint syncPoint, long timeout, String message) throws InterruptedException, TimeoutException, AssertionFailedError { + try { + return syncPoint.waitForResults(timeout, message); + } catch (InterruptedException | TimeoutException e) { + throw e; + } catch (Exception e) { + throw new AssertionFailedError(message, e); + } + } } diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackLowLevelIntegrationTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackLowLevelIntegrationTest.java index d517494db..480129575 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackLowLevelIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackLowLevelIntegrationTest.java @@ -50,7 +50,7 @@ public abstract class AbstractSmackLowLevelIntegrationTest extends AbstractSmack * Get a connected connection. Note that this method will return a connection constructed via the default connection * descriptor. It is primarily meant for integration tests to discover if the XMPP service supports a certain * feature, that the integration test requires to run. This feature discovery is typically done in the constructor of the - * integration tests. And if the discovery fails a {@link TestNotPossibleException} should be thrown by he constructor. + * integration tests. And if the discovery fails a {@link TestNotPossibleException} should be thrown by the constructor. * *

    Please ensure that you invoke {@link #recycle(AbstractXMPPConnection connection)} once you are done with this connection. * diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java index 0df16141c..fab1e00c9 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2023 Florian Schmaus + * Copyright 2015-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,20 +22,24 @@ import java.io.IOException; import java.lang.reflect.Method; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.net.ssl.SSLContext; import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; import org.jivesoftware.smack.debugger.ConsoleDebugger; +import org.jivesoftware.smack.debugger.SmackDebuggerFactory; import org.jivesoftware.smack.util.CollectionUtil; import org.jivesoftware.smack.util.Function; import org.jivesoftware.smack.util.Objects; @@ -61,12 +65,6 @@ public final class Configuration { serviceAdministration, } - public enum Debugger { - none, - console, - enhanced, - } - public enum DnsResolver { minidns, javax, @@ -75,6 +73,8 @@ public final class Configuration { public final DomainBareJid service; + public final String host; + public final String serviceTlsPin; public final SslContextFactory sslContextFactory; @@ -101,7 +101,7 @@ public final class Configuration { public final String accountThreePassword; - public final Debugger debugger; + public final SmackDebuggerFactory debuggerFactory; public final Set enabledTests; @@ -111,6 +111,10 @@ public final class Configuration { private final Map> disabledTestsMap; + public final Set enabledSpecifications; + + public final Set disabledSpecifications; + public final String defaultConnectionNickname; public final Set enabledConnections; @@ -132,9 +136,12 @@ public final class Configuration { public final CompatibilityMode compatibilityMode; + public final List testRunResultProcessors; + private Configuration(Configuration.Builder builder) throws KeyManagementException, NoSuchAlgorithmException { service = Objects.requireNonNull(builder.service, "'service' must be set. Either via 'properties' files or via system property 'sinttest.service'."); + host = builder.host; serviceTlsPin = builder.serviceTlsPin; if (serviceTlsPin != null) { SSLContext sslContext = Java7Pinning.forPin(serviceTlsPin); @@ -148,7 +155,7 @@ public final class Configuration { } else { replyTimeout = 47000; } - debugger = builder.debugger; + debuggerFactory = builder.debuggerFactory; if (StringUtils.isNotEmpty(builder.adminAccountUsername, builder.adminAccountPassword)) { accountRegistration = AccountRegistration.serviceAdministration; } @@ -181,6 +188,8 @@ public final class Configuration { this.enabledTestsMap = convertTestsToMap(enabledTests); this.disabledTests = CollectionUtil.nullSafeUnmodifiableSet(builder.disabledTests); this.disabledTestsMap = convertTestsToMap(disabledTests); + this.enabledSpecifications = CollectionUtil.nullSafeUnmodifiableSet(builder.enabledSpecifications); + this.disabledSpecifications = CollectionUtil.nullSafeUnmodifiableSet(builder.disabledSpecifications); this.defaultConnectionNickname = builder.defaultConnectionNickname; this.enabledConnections = builder.enabledConnections; this.disabledConnections = builder.disabledConnections; @@ -192,17 +201,12 @@ public final class Configuration { } b.setSecurityMode(securityMode); b.setXmppDomain(service); + if (host != null) { + b.setHost(host); + } - switch (debugger) { - case enhanced: - b.setDebuggerFactory(EnhancedDebugger.Factory.INSTANCE); - break; - case console: - b.setDebuggerFactory(ConsoleDebugger.Factory.INSTANCE); - break; - case none: - // Nothing to do :). - break; + if (debuggerFactory != null) { + b.setDebuggerFactory(debuggerFactory); } }; @@ -210,6 +214,7 @@ public final class Configuration { this.dnsResolver = builder.dnsResolver; this.compatibilityMode = builder.compatibilityMode; + this.testRunResultProcessors = builder.testRunResultProcessors; } public boolean isAccountRegistrationPossible() { @@ -224,6 +229,8 @@ public final class Configuration { private DomainBareJid service; + private String host; + private String serviceTlsPin; private SecurityMode securityMode; @@ -246,12 +253,16 @@ public final class Configuration { public String accountThreePassword; - private Debugger debugger = Debugger.none; + private SmackDebuggerFactory debuggerFactory; private Set enabledTests; private Set disabledTests; + private Set enabledSpecifications; + + private Set disabledSpecifications; + private String defaultConnectionNickname; private Set enabledConnections; @@ -266,6 +277,8 @@ public final class Configuration { private CompatibilityMode compatibilityMode = CompatibilityMode.standardsCompliant; + private List testRunResultProcessors; + private Builder() { } @@ -283,6 +296,11 @@ public final class Configuration { return this; } + private Builder setHost(String host) { + this.host = host; + return this; + } + public Builder addEnabledTest(Class enabledTest) { if (enabledTests == null) { enabledTests = new HashSet<>(); @@ -315,7 +333,7 @@ public final class Configuration { this.accountOneUsername = StringUtils.requireNotNullNorEmpty(accountOneUsername, "accountOneUsername must not be null nor empty"); this.accountOnePassword = StringUtils.requireNotNullNorEmpty(accountOnePassword, "accountOnePassword must not be null nor empty"); this.accountTwoUsername = StringUtils.requireNotNullNorEmpty(accountTwoUsername, "accountTwoUsername must not be null nor empty"); - this.accountTwoPassword = StringUtils.requireNotNullNorEmpty(accountTwoPassword, "accountTwoPasswordmust not be null nor empty"); + this.accountTwoPassword = StringUtils.requireNotNullNorEmpty(accountTwoPassword, "accountTwoPassword must not be null nor empty"); this.accountThreeUsername = StringUtils.requireNotNullNorEmpty(accountThreeUsername, "accountThreeUsername must not be null nor empty"); this.accountThreePassword = StringUtils.requireNotNullNorEmpty(accountThreePassword, "accountThreePassword must not be null nor empty"); return this; @@ -352,18 +370,23 @@ public final class Configuration { case "false": // For backwards compatibility settings with previous boolean setting. LOGGER.warning("Debug string \"" + debuggerString + "\" is deprecated, please use \"none\" instead"); case "none": - debugger = Debugger.none; + debuggerFactory = null; break; case "true": // For backwards compatibility settings with previous boolean setting. LOGGER.warning("Debug string \"" + debuggerString + "\" is deprecated, please use \"console\" instead"); case "console": - debugger = Debugger.console; + debuggerFactory = ConsoleDebugger.Factory.INSTANCE; break; case "enhanced": - debugger = Debugger.enhanced; + debuggerFactory = EnhancedDebugger.Factory.INSTANCE; break; default: - throw new IllegalArgumentException("Unrecognized debugger string: " + debuggerString); + try { + final Class aClass = Class.forName(debuggerString).asSubclass(SmackDebuggerFactory.class); + debuggerFactory = aClass.getConstructor().newInstance(); + } catch (Exception e) { + throw new IllegalArgumentException("Unable to construct debugger from value: " + debuggerString, e); + } } return this; } @@ -378,6 +401,16 @@ public final class Configuration { return this; } + public Builder setEnabledSpecifications(String enabledSpecificationsString) { + enabledSpecifications = getSpecificationSetFrom(enabledSpecificationsString); + return this; + } + + public Builder setDisabledSpecifications(String disabledSpecificationsString) { + disabledSpecifications = getSpecificationSetFrom(disabledSpecificationsString); + return this; + } + public Builder setDefaultConnection(String defaultConnectionNickname) { this.defaultConnectionNickname = defaultConnectionNickname; return this; @@ -461,6 +494,15 @@ public final class Configuration { return setCompatibilityMode(compatibilityMode); } + public Builder setTestRunResultProcessors(String testRunResultProcessorsString) { + if (testRunResultProcessorsString == null) { + return this; + } + + testRunResultProcessors = getTestRunProcessorListFrom(testRunResultProcessorsString); + return this; + } + public Configuration build() throws KeyManagementException, NoSuchAlgorithmException { return new Configuration(this); } @@ -493,6 +535,7 @@ public final class Configuration { Builder builder = builder(); builder.setService(properties.getProperty("service")); + builder.setHost(properties.getProperty("host")); builder.setServiceTlsPin(properties.getProperty("serviceTlsPin")); builder.setSecurityMode(properties.getProperty("securityMode")); builder.setReplyTimeout(properties.getProperty("replyTimeout", "47000")); @@ -517,12 +560,14 @@ public final class Configuration { String debugString = properties.getProperty("debug"); if (debugString != null) { - LOGGER.warning("Usage of depreacted 'debug' option detected, please use 'debugger' instead"); + LOGGER.warning("Usage of deprecated 'debug' option detected, please use 'debugger' instead"); builder.setDebugger(debugString); } builder.setDebugger(properties.getProperty("debugger")); builder.setEnabledTests(properties.getProperty("enabledTests")); builder.setDisabledTests(properties.getProperty("disabledTests")); + builder.setEnabledSpecifications(properties.getProperty("enabledSpecifications")); + builder.setDisabledSpecifications(properties.getProperty("disabledSpecifications")); builder.setDefaultConnection(properties.getProperty("defaultConnection")); builder.setEnabledConnections(properties.getProperty("enabledConnections")); builder.setDisabledConnections(properties.getProperty("disabledConnections")); @@ -536,11 +581,14 @@ public final class Configuration { builder.setCompatibilityMode(properties.getProperty("compatibilityMode")); + builder.setTestRunResultProcessors(properties.getProperty("testRunResultProcessors", + SmackIntegrationTestFramework.JulTestRunResultProcessor.class.getName())); + return builder.build(); } private static File findPropertiesFile() { - List possibleLocations = new LinkedList<>(); + List possibleLocations = new ArrayList<>(); possibleLocations.add("properties"); String userHome = System.getProperty("user.home"); if (userHome != null) { @@ -587,6 +635,23 @@ public final class Configuration { }); } + private static Set getSpecificationSetFrom(String input) { + return split(input, Configuration::normalizeSpecification); + } + + private static List getTestRunProcessorListFrom(String input) { + return Arrays.stream(input.split(",")) + .map(element -> { + try { + final Class aClass = Class.forName(element).asSubclass(SmackIntegrationTestFramework.TestRunResultProcessor.class); + return aClass.getConstructor().newInstance(); + } catch (Exception e) { + throw new IllegalArgumentException("Unable to construct TestRunResultProcessor from value: " + element, e); + } + }) + .collect(Collectors.toList()); + } + private static Map> convertTestsToMap(Set tests) { Map> res = new HashMap<>(); for (String test : tests) { @@ -695,4 +760,34 @@ public final class Configuration { return contains(method, disabledTestsMap); } + public boolean isSpecificationEnabled(String specification) { + if (enabledSpecifications.isEmpty()) { + return true; + } + + if (specification == null) { + return false; + } + + return enabledSpecifications.contains(normalizeSpecification(specification)); + } + + public boolean isSpecificationDisabled(String specification) { + if (disabledSpecifications.isEmpty()) { + return false; + } + + if (specification == null) { + return false; + } + + return disabledSpecifications.contains(normalizeSpecification(specification)); + } + + public static String normalizeSpecification(String specification) { + if (specification == null || specification.isBlank()) { + return null; + } + return specification.replaceAll("[\\s-]", "").toUpperCase(Locale.ROOT); + } } diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java index 8be908fb0..3fb4edaae 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2023 Florian Schmaus + * Copyright 2015-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,11 +39,12 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; @@ -64,6 +65,7 @@ import org.jivesoftware.smack.util.dns.dnsjava.DNSJavaResolver; import org.jivesoftware.smack.util.dns.javax.JavaxResolver; import org.jivesoftware.smack.util.dns.minidns.MiniDnsResolver; +import org.jivesoftware.smackx.debugger.EnhancedDebugger; import org.jivesoftware.smackx.debugger.EnhancedDebuggerWindow; import org.jivesoftware.smackx.iqregister.AccountManager; @@ -71,6 +73,7 @@ import org.igniterealtime.smack.inttest.Configuration.AccountRegistration; import org.igniterealtime.smack.inttest.annotations.AfterClass; import org.igniterealtime.smack.inttest.annotations.BeforeClass; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.reflections.Reflections; import org.reflections.scanners.MethodAnnotationsScanner; import org.reflections.scanners.MethodParameterScanner; @@ -87,6 +90,8 @@ public class SmackIntegrationTestFramework { public static boolean SINTTEST_UNIT_TEST = false; + private static ConcreteTest TEST_UNDER_EXECUTION; + protected final Configuration config; protected TestRunResult testRunResult; @@ -108,47 +113,86 @@ public class SmackIntegrationTestFramework { SmackIntegrationTestFramework sinttest = new SmackIntegrationTestFramework(config); TestRunResult testRunResult = sinttest.run(); - for (Map.Entry, Throwable> entry : testRunResult.impossibleTestClasses.entrySet()) { - LOGGER.info("Could not run " + entry.getKey().getName() + " because: " - + entry.getValue().getLocalizedMessage()); - } - for (TestNotPossible testNotPossible : testRunResult.impossibleIntegrationTests) { - LOGGER.info("Could not run " + testNotPossible.concreteTest + " because: " - + testNotPossible.testNotPossibleException.getMessage()); - } - for (SuccessfulTest successfulTest : testRunResult.successfulIntegrationTests) { - LOGGER.info(successfulTest.concreteTest + " ✔"); - } - final int successfulTests = testRunResult.successfulIntegrationTests.size(); - final int failedTests = testRunResult.failedIntegrationTests.size(); - final int availableTests = testRunResult.getNumberOfAvailableTests(); - LOGGER.info("SmackIntegrationTestFramework[" + testRunResult.testRunId + ']' + " finished: " - + successfulTests + '/' + availableTests + " [" + failedTests + " failed]"); - - final int exitStatus; - if (failedTests > 0) { - LOGGER.warning("💀 The following " + failedTests + " tests failed! 💀"); - for (FailedTest failedTest : testRunResult.failedIntegrationTests) { - final Throwable cause = failedTest.failureReason; - LOGGER.log(Level.SEVERE, failedTest.concreteTest + " failed: " + cause, cause); + for (final TestRunResultProcessor testRunResultProcessor : config.testRunResultProcessors) { + try { + testRunResultProcessor.process(testRunResult); + } catch (Throwable t) { + LOGGER.log(Level.WARNING, "Invocation of TestRunResultProcessor " + testRunResultProcessor + " failed.", t); } - exitStatus = 2; - } else { - LOGGER.info("All possible Smack Integration Tests completed successfully. \\o/"); - exitStatus = 0; } - switch (config.debugger) { - case enhanced: + if (config.debuggerFactory instanceof EnhancedDebugger) { EnhancedDebuggerWindow.getInstance().waitUntilClosed(); - break; - default: - break; } + final int exitStatus = testRunResult.failedIntegrationTests.isEmpty() ? 0 : 2; System.exit(exitStatus); } + public static class JulTestRunResultProcessor implements TestRunResultProcessor { + + @Override + public void process(final TestRunResult testRunResult) { + for (Map.Entry, Throwable> entry : testRunResult.impossibleTestClasses.entrySet()) { + LOGGER.info("Could not run " + entry.getKey().getName() + " because: " + + entry.getValue().getLocalizedMessage()); + } + for (TestNotPossible testNotPossible : testRunResult.impossibleIntegrationTests) { + LOGGER.info("Could not run " + testNotPossible.concreteTest + " because: " + + testNotPossible.testNotPossibleException.getMessage()); + } + for (SuccessfulTest successfulTest : testRunResult.successfulIntegrationTests) { + LOGGER.info(successfulTest.concreteTest + " ✔"); + } + final int successfulTests = testRunResult.successfulIntegrationTests.size(); + final int failedTests = testRunResult.failedIntegrationTests.size(); + final int availableTests = testRunResult.getNumberOfAvailableTests(); + LOGGER.info("SmackIntegrationTestFramework[" + testRunResult.testRunId + ']' + " finished: " + + successfulTests + '/' + availableTests + " [" + failedTests + " failed]"); + + if (failedTests > 0) { + LOGGER.warning("💀 The following " + failedTests + " tests failed! 💀"); + final SortedSet bySpecification = new TreeSet<>(); + for (FailedTest failedTest : testRunResult.failedIntegrationTests) { + final Throwable cause = failedTest.failureReason; + LOGGER.log(Level.SEVERE, failedTest.concreteTest + " failed: " + cause, cause); + if (failedTest.concreteTest.method.getDeclaringClass().isAnnotationPresent(SpecificationReference.class)) { + final String specificationReference = getSpecificationReference(failedTest.concreteTest.method); + if (specificationReference != null) { + bySpecification.add("- " + specificationReference + " (as tested by '" + failedTest.concreteTest + "')"); + } + } + } + if (!bySpecification.isEmpty()) { + LOGGER.log(Level.SEVERE, "The failed tests correspond to the following specifications:" + System.lineSeparator() + String.join(System.lineSeparator(), bySpecification)); + } + } else { + LOGGER.info("All possible Smack Integration Tests completed successfully. \\o/"); + } + } + } + + private static String getSpecificationReference(Method method) { + final SpecificationReference spec = method.getDeclaringClass().getAnnotation(SpecificationReference.class); + if (spec == null || spec.document().isBlank()) { + return null; + } + String line = spec.document().trim(); + if (!spec.version().isBlank()) { + line += " (version " + spec.version() + ")"; + } + + final SmackIntegrationTest test = method.getAnnotation(SmackIntegrationTest.class); + if (!test.section().isBlank()) { + line += " section " + test.section().trim(); + } + if (!test.quote().isBlank()) { + line += ":\t\"" + test.quote().trim() + "\""; + } + assert !line.isBlank(); + return line; + } + public SmackIntegrationTestFramework(Configuration configuration) { this.config = configuration; } @@ -175,7 +219,7 @@ public class SmackIntegrationTestFramework { this.connectionManager = new XmppConnectionManager(this); LOGGER.info("SmackIntegrationTestFramework [" + testRunResult.testRunId + ']' + ": Starting\nSmack version: " + Smack.getVersion()); - if (config.debugger != Configuration.Debugger.none) { + if (config.debuggerFactory != null) { // JUL Debugger will not print any information until configured to print log messages of // level FINE // TODO configure JUL for log? @@ -192,7 +236,7 @@ public class SmackIntegrationTestFramework { String[] testPackages; if (config.testPackages == null || config.testPackages.isEmpty()) { - testPackages = new String[] { "org.jivesoftware.smackx", "org.jivesoftware.smack" }; + testPackages = new String[] { "org.jivesoftware.smackx", "org.jivesoftware.smack", "org.igniterealtime.smackx", "org.igniterealtime.smack" }; } else { testPackages = config.testPackages.toArray(new String[config.testPackages.size()]); @@ -244,6 +288,10 @@ public class SmackIntegrationTestFramework { return testRunResult; } + public static ConcreteTest getTestUnderExecution() { + return TEST_UNDER_EXECUTION; + } + @SuppressWarnings({"Finally"}) private void runTests(Set> classes) throws InterruptedException, InstantiationException, IllegalAccessException, @@ -292,7 +340,27 @@ public class SmackIntegrationTestFramework { } if (config.isClassDisabled(testClass)) { - DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test class " + testClassName + " because it is disalbed"); + DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test class " + testClassName + " because it is disabled"); + testRunResult.disabledTestClasses.add(disabledTestClass); + continue; + } + + final String specification; + if (testClass.isAnnotationPresent(SpecificationReference.class)) { + final SpecificationReference specificationReferenceAnnotation = testClass.getAnnotation(SpecificationReference.class); + specification = Configuration.normalizeSpecification(specificationReferenceAnnotation.document()); + } else { + specification = null; + } + + if (!config.isSpecificationEnabled(specification)) { + DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test method " + testClass + " because it tests a specification ('" + specification + "') that is not enabled"); + testRunResult.disabledTestClasses.add(disabledTestClass); + continue; + } + + if (config.isSpecificationDisabled(specification)) { + DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test method " + testClass + " because it tests a specification ('" + specification + "') that is disabled"); testRunResult.disabledTestClasses.add(disabledTestClass); continue; } @@ -363,7 +431,7 @@ public class SmackIntegrationTestFramework { final Class[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length > 0) { throw new IllegalStateException( - "SmackIntegrationTest annotaton on " + method + " that takes arguments "); + "SmackIntegrationTest annotation on " + method + " that takes arguments "); } break; case LowLevel: @@ -458,6 +526,11 @@ public class SmackIntegrationTestFramework { } sb.append('\n'); } + + if (numberOfAvailableTests == 0) { + throw new IllegalArgumentException("No integration tests selected."); + } + sb.append("Available tests: ").append(numberOfAvailableTests); if (!testRunResult.disabledTestClasses.isEmpty() || !testRunResult.disabledTests.isEmpty()) { sb.append(" (Disabled ").append(testRunResult.disabledTestClasses.size()).append(" classes") @@ -494,7 +567,7 @@ public class SmackIntegrationTestFramework { return; } Throwable nonFatalFailureReason; - // junit assert's throw an AssertionError if they fail, those should not be + // junit asserts throw an AssertionError if they fail, those should not be // thrown up, as it would be done by throwFatalException() if (cause instanceof AssertionError) { nonFatalFailureReason = cause; @@ -594,8 +667,8 @@ public class SmackIntegrationTestFramework { throw (InterruptedException) e; } - // We handle NullPointerException as a non fatal exception, as they are mostly caused by an invalid reply where - // a extension element is missing. Consider for example + // We handle NullPointerException as a non-fatal exception, as they are mostly caused by an invalid reply where + // an extension element is missing. Consider for example // assertEquals(StanzaError.Condition.foo, response.getError().getCondition()) // Otherwise NPEs could be caused by an internal bug in Smack, e.g. missing null handling. if (e instanceof NullPointerException) { @@ -610,18 +683,23 @@ public class SmackIntegrationTestFramework { return (Exception) e; } + @FunctionalInterface + public interface TestRunResultProcessor { + void process(SmackIntegrationTestFramework.TestRunResult testRunResult); + } + public static final class TestRunResult { /** - * A short String of lowercase characters and numbers used to identify a integration test + * A short String of lowercase characters and numbers used to identify an integration test * run. We use lowercase characters because this string will eventually be part of the - * localpart of the used JIDs (and the localpart is case insensitive). + * localpart of the used JIDs (and the localpart is case-insensitive). */ public final String testRunId = StringUtils.insecureRandomString(5).toLowerCase(Locale.US); - private final List successfulIntegrationTests = Collections.synchronizedList(new LinkedList()); - private final List failedIntegrationTests = Collections.synchronizedList(new LinkedList()); - private final List impossibleIntegrationTests = Collections.synchronizedList(new LinkedList()); + private final List successfulIntegrationTests = Collections.synchronizedList(new ArrayList()); + private final List failedIntegrationTests = Collections.synchronizedList(new ArrayList()); + private final List impossibleIntegrationTests = Collections.synchronizedList(new ArrayList()); // TODO: Ideally three would only be a list of disabledTests, but since we do not process a disabled test class // any further, we can not determine the concrete disabled tests. @@ -680,7 +758,12 @@ public class SmackIntegrationTestFramework { executeSinttestSpecialMethod(beforeClassMethod); for (ConcreteTest concreteTest : concreteTests) { - runConcreteTest(concreteTest); + TEST_UNDER_EXECUTION = concreteTest; + try { + runConcreteTest(concreteTest); + } finally { + TEST_UNDER_EXECUTION = null; + } } } finally { @@ -729,21 +812,33 @@ public class SmackIntegrationTestFramework { return null; } - static final class ConcreteTest { + public static final class ConcreteTest { private final TestType testType; private final Method method; private final Executor executor; - private final String[] subdescriptons; + private final List subdescriptons; private ConcreteTest(TestType testType, Method method, Executor executor, String... subdescriptions) { this.testType = testType; this.method = method; this.executor = executor; - this.subdescriptons = subdescriptions; + this.subdescriptons = List.of(subdescriptions); } private transient String stringCache; + public TestType getTestType() { + return testType; + } + + public Method getMethod() { + return method; + } + + public List getSubdescriptons() { + return subdescriptons; + } + @Override public String toString() { if (stringCache != null) { @@ -756,9 +851,9 @@ public class SmackIntegrationTestFramework { .append(method.getName()) .append(" (") .append(testType.name()); - if (subdescriptons != null && subdescriptons.length > 0) { + if (!subdescriptons.isEmpty()) { sb.append(", "); - StringUtils.appendTo(Arrays.asList(subdescriptons), sb); + StringUtils.appendTo(subdescriptons, sb); } sb.append(')'); diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/TestNotPossibleException.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/TestNotPossibleException.java index 843726ca8..cf7030efd 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/TestNotPossibleException.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/TestNotPossibleException.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015 Florian Schmaus + * Copyright 2015-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,4 +26,12 @@ public class TestNotPossibleException extends Exception { public TestNotPossibleException(String reason) { super(reason); } + + public TestNotPossibleException(Throwable reason) { + super(reason); + } + + public TestNotPossibleException(String message, Throwable reason) { + super(message, reason); + } } diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionDescriptor.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionDescriptor.java index 173407429..adcdcaa40 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionDescriptor.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionDescriptor.java @@ -155,7 +155,7 @@ public final class XmppConnectionDescriptor< return XmppConnectionDescriptor.buildWith(ModularXmppClientToServerConnection.class, ModularXmppClientToServerConnectionConfiguration.class, ModularXmppClientToServerConnectionConfiguration.Builder.class) .withNickname(nickname) - .applyExtraConfguration(cb -> { + .applyExtraConfiguration(cb -> { cb.removeAllModules(); ModularXmppClientToServerConnectionModuleDescriptor webSocketModuleDescriptor = XmppWebSocketTransportModuleDescriptor.getBuilder(cb) @@ -184,7 +184,13 @@ public final class XmppConnectionDescriptor< nickname = connectionClass.getSimpleName(); } + // TODO Remove in Smack 4.6 + @Deprecated // Replaced by applyExtraConfiguration(Consumer extraBuilder) public Builder applyExtraConfguration(Consumer extraBuilder) { + return applyExtraConfiguration(extraBuilder); + } + + public Builder applyExtraConfiguration(Consumer extraBuilder) { this.extraBuilder = extraBuilder; return this; } diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java index 0653e5d14..3b2f21b60 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java @@ -85,7 +85,7 @@ public class XmppConnectionManager { addConnectionDescriptor( XmppConnectionDescriptor.buildWith(ModularXmppClientToServerConnection.class, ModularXmppClientToServerConnectionConfiguration.class, ModularXmppClientToServerConnectionConfiguration.Builder.class) .withNickname("modular-nocompress") - .applyExtraConfguration(cb -> cb.removeModule(CompressionModuleDescriptor.class)) + .applyExtraConfiguration(cb -> cb.removeModule(CompressionModuleDescriptor.class)) .build() ); addConnectionDescriptor( diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/annotations/SmackIntegrationTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/annotations/SmackIntegrationTest.java index 173b4d2e9..55b1ef684 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/annotations/SmackIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/annotations/SmackIntegrationTest.java @@ -31,4 +31,18 @@ public @interface SmackIntegrationTest { int connectionCount() default -1; + /** + * Unique identifier for a section (or paragraph) of the document referenced by {@link SpecificationReference}, + * such as '6.2.1'. + * + * @return a document section identifier + */ + String section() default ""; + + /** + * A quotation of relevant text from the section referenced by {@link #section()}. + * + * @return human-readable text from the references document and section. + */ + String quote() default ""; } diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/annotations/SpecificationReference.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/annotations/SpecificationReference.java new file mode 100644 index 000000000..beadb7782 --- /dev/null +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/annotations/SpecificationReference.java @@ -0,0 +1,48 @@ +/** + * + * Copyright 2024 Guus der Kinderen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.igniterealtime.smack.inttest.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Reference to a specification document. + * + * @author Guus der Kinderen, guus@goodbytes.nl + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface SpecificationReference { + + /** + * Unique identifier for a specification document, such as 'RFC 6120' or 'XEP-0485'. + * + * @return a document identifier + */ + String document(); + + /** + * An optional version number, such as '1.2.0'. + * + * @return a version number + */ + String version() default ""; +} diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/package-info.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/package-info.java index 2caf8d792..84301a2f5 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/package-info.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/package-info.java @@ -80,6 +80,10 @@ * XMPP service to run the tests on * * + * host + * IP address or DNS name of the XMPP service to run the tests on + * + * * serviceTlsPin * TLS Pin (used by java-pinning) * @@ -125,7 +129,7 @@ * * * debugger - * ‘console’ for console debugger, ‘enhanced’ for the enhanced debugger + * ‘console’ for console debugger, ‘enhanced’ for the enhanced debugger, or the name of a class that implements SmackDebuggerFactory for a custom debugger * * * enabledTests @@ -136,6 +140,14 @@ * List of disabled tests * * + * enabledSpecifications + * List of specifications for which to enable tests + * + * + * disabledSpecifications + * List of specifications for which to disable tests + * + * * defaultConnection * Nickname of the default connection * @@ -159,6 +171,10 @@ * dnsResolver * One of ‘minidns’, ‘javax’ or ‘dnsjava’. Defaults to ‘minidns’. * + * + * testRunResultProcessors + * List of class names for generating test run output. Defaults to 'org.igniterealtime.smack.inttest.SmackIntegrationTestFramework$ConsoleTestRunResultProcessor' + * * *

    Where to place the properties file

    *

    @@ -187,6 +203,20 @@ *

    * would run all tests defined in the SoftwareInfoIntegrationTest class. *

    + *

    + * Use enabledSpecifications to run all tests that assert implementation of functionality that is described + * in standards identified by the provided specification-reference. + *

    + *

    + * For example: + *

    + * + *
    + * $ gradle integrationTest -Dsinttest.enabledSpecifications=XEP-0045
    + * 
    + *

    + * would run all tests that are annotated to verify functionality specified in XEP-0045: "Multi-User Chat". + *

    *

    Overview of the components

    *

    * Package org.igniterealtime.smack.inttest @@ -249,7 +279,7 @@ * the required XMPP feature. If it does not, simply throw a TestNotPossibleException. *

    *

    - * Test methods must be public, take zero arguments i.e. declare no parameters and be annoated with + * Test methods must be public, take zero arguments i.e. declare no parameters and be annotated with * @SmackIntegrationTest. If the test method is not able to perform a test then it should throw a * TestNotPossibleException. *

    @@ -266,7 +296,7 @@ *

    *

    Low-Level Integration Tests

    *

    - * Classes that implement low-level integration tests need to sublcass + * Classes that implement low-level integration tests need to subclass * {@link org.igniterealtime.smack.inttest.AbstractSmackLowLevelIntegrationTest}. The test methods can declare as many * parameters as they need to, but every parameter must be of type XMPPTCPConnection. The framework will * automatically create, register and login the connections. After the test is finished, the connections will be @@ -284,6 +314,18 @@ * Debug Window launching when your tests launch, and you'll get a stanza-by-stanza account of what happened on each * connection, hopefully enough to diagnose what went wrong. *

    + *

    + * Lastly, you can provide a custom debugger, by providing the fully qualified name of a class that implements + * {@link org.jivesoftware.smack.debugger.SmackDebuggerFactory}. The provided factory must declare a public constructor + * that takes no arguments. + *

    + *

    + * Example: + *

    + * + *
    {@code
    + * $ gradle integrationTest -Dsinttest.service=my.xmppservice.org -Dsinttest.debugger="org.example.MyDebugger$Factory"
    + * }
    *

    Debugging in the IDE

    *

    * If the output isn't enough, you may need to debug and inspect running code within the IDE. Depending on the IDE, in @@ -302,7 +344,20 @@ *

    * *
    {@code
    - * $ gradle integrationTest -Dsinttest.service=my.xmppserivce.org -Dsinttest.testPackages=org.mypackage,org.otherpackage
    + * $ gradle integrationTest -Dsinttest.service=my.xmppservice.org -Dsinttest.testPackages=org.mypackage,org.otherpackage
    + * }
    + *

    Generating test run reports

    + *

    + * By default, the results of the test run is printed to standard-error. You can, however, provide your own processing + * for the test run results. To do so, create an implementation of + * {@link org.igniterealtime.smack.inttest.SmackIntegrationTestFramework.TestRunResultProcessor} and provide its class + * name to the testRunResultProcessor property. This property takes a comma separated list of class names. + *

    + *

    + * Example: + *

    + *
    {@code
    + * $ gradle integrationTest -Dsinttest.service=my.xmppservice.org -Dsinttest.testRunResultProcessor=org.igniterealtime.smack.inttest.SmackIntegrationTestFramework$ConsoleTestRunResultProcessor
      * }
    */ package org.igniterealtime.smack.inttest; diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/IntegrationTestRosterUtil.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/IntegrationTestRosterUtil.java index 4a6569dcf..befa9eb23 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/IntegrationTestRosterUtil.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/IntegrationTestRosterUtil.java @@ -79,7 +79,7 @@ public class IntegrationTestRosterUtil { try { presenceRequestingRoster.sendSubscriptionRequest(presenceRequestReceiverAddress.asBareJid()); - syncPoint.waitForResult(timeout); + syncPoint.waitForResult(timeout, "Timeout while waiting for subscription request of '" + presenceRequestingAddress + "' to '" + presenceRequestReceiverAddress + "' to be answered."); } finally { presenceRequestReceiverRoster.removeSubscribeListener(subscribeListener); presenceRequestingRoster.removePresenceEventListener(presenceEventListener); diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/MultiResultSyncPoint.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/MultiResultSyncPoint.java new file mode 100644 index 000000000..aaf0bd4f1 --- /dev/null +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/MultiResultSyncPoint.java @@ -0,0 +1,71 @@ +/** + * + * Copyright 2021-2024 Guus der Kinderen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.igniterealtime.smack.inttest.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeoutException; + +import org.jivesoftware.smack.util.Objects; + +public class MultiResultSyncPoint { + + private final List results; + private E exception; + private final int expectedResultCount; + + public MultiResultSyncPoint(int expectedResultCount) { + this.expectedResultCount = expectedResultCount; + this.results = new ArrayList<>(expectedResultCount); + } + + public synchronized List waitForResults(long timeout) throws E, InterruptedException, TimeoutException { + return waitForResults(timeout, null); + } + + public synchronized List waitForResults(long timeout, String timeoutMessage) throws E, InterruptedException, TimeoutException { + long now = System.currentTimeMillis(); + final long deadline = now + timeout; + while (results.size() < expectedResultCount && exception == null && now < deadline) { + wait(deadline - now); + now = System.currentTimeMillis(); + } + if (now >= deadline) { + StringBuilder sb = new StringBuilder(); + if (timeoutMessage != null) { + sb.append(timeoutMessage).append(". "); + } + sb.append("MultiResultSyncPoint timeout waiting " + timeout + " ms. Got " + results.size() + " results of " + expectedResultCount + " results"); + + throw new TimeoutException(sb.toString()); + } + if (exception != null) throw exception; + return new ArrayList<>(results); + } + + public synchronized void signal(R result) { + this.results.add(Objects.requireNonNull(result)); + if (expectedResultCount <= results.size()) { + notifyAll(); + } + } + + public synchronized void signal(E exception) { + this.exception = Objects.requireNonNull(exception); + notifyAll(); + } +} diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/ResultSyncPoint.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/ResultSyncPoint.java index 37b143934..ee691bf90 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/ResultSyncPoint.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/util/ResultSyncPoint.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015 Florian Schmaus + * Copyright 2015-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,10 @@ public class ResultSyncPoint { private E exception; public R waitForResult(long timeout) throws E, InterruptedException, TimeoutException { + return waitForResult(timeout, null); + } + + public R waitForResult(long timeout, String timeoutMessage) throws E, InterruptedException, TimeoutException { synchronized (this) { if (result != null) { return result; @@ -46,7 +50,12 @@ public class ResultSyncPoint { if (exception != null) { throw exception; } - throw new TimeoutException("Timeout expired"); + + String message = "Timeout after " + timeout + "ms"; + if (timeoutMessage != null) { + message += ": " + timeoutMessage; + } + throw new TimeoutException(message); } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/ChatTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/ChatTest.java index d956c92ee..56a5ec10e 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smack/ChatTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/ChatTest.java @@ -66,8 +66,8 @@ public class ChatTest extends AbstractSmackIntegrationTest { JivePropertiesManager.setJavaObjectEnabled(false); } - @SuppressWarnings("deprecation") @SmackIntegrationTest + @SuppressWarnings({"deprecation", "JavaUtilDate"}) public void testProperties() throws Exception { org.jivesoftware.smack.chat.Chat newChat = chatManagerOne.createChat(conTwo.getUser()); StanzaCollector collector = conTwo.createStanzaCollector(new ThreadFilter(newChat.getThreadID())); diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/LoginIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/LoginIntegrationTest.java index 2c9dbb9ec..1e75154a9 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smack/LoginIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/LoginIntegrationTest.java @@ -48,7 +48,7 @@ public class LoginIntegrationTest extends AbstractSmackLowLevelIntegrationTest { * @throws IOException if an I/O error occurred. * @throws SmackException if Smack detected an exceptional situation. * @throws NoSuchAlgorithmException if no such algorithm is available. - * @throws KeyManagementException if there was a key mangement error. + * @throws KeyManagementException if there was a key management error. */ @SmackIntegrationTest public void testInvalidLogin(UnconnectedConnectionSource unconnectedConnectionSource) throws SmackException, IOException, XMPPException, @@ -64,7 +64,8 @@ public class LoginIntegrationTest extends AbstractSmackLowLevelIntegrationTest { () -> connection.login(nonExistentUserString, invalidPassword)); SaslNonza.SASLFailure saslFailure = saslErrorException.getSASLFailure(); - assertEquals(SASLError.not_authorized, saslFailure.getSASLError()); + assertEquals(SASLError.not_authorized, saslFailure.getSASLError(), + "Expected the server to return the appropriate SASL failure condition (but it did not)"); } finally { connection.disconnect(); } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/StreamManagementTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/StreamManagementTest.java index c9ca9095b..fdbb755c7 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smack/StreamManagementTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/StreamManagementTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2020 Florian Schmaus + * Copyright 2015-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,9 +32,11 @@ import org.igniterealtime.smack.inttest.AbstractSmackSpecificLowLevelIntegration import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.jxmpp.jid.EntityFullJid; public class StreamManagementTest extends AbstractSmackSpecificLowLevelIntegrationTest { + @SuppressWarnings("this-escape") public StreamManagementTest(SmackIntegrationTestEnvironment environment) throws Exception { super(environment, XMPPTCPConnection.class); XMPPTCPConnection connection = getSpecificUnconnectedConnection(); @@ -57,7 +59,7 @@ public class StreamManagementTest extends AbstractSmackSpecificLowLevelIntegrati try { send(body1, conOne, conTwo); - assertMessageWithBodyReceived(body1, collector); + assertMessageWithBodyReceived(body1, collector, conTwo.getUser()); conOne.instantShutdown(); @@ -65,10 +67,10 @@ public class StreamManagementTest extends AbstractSmackSpecificLowLevelIntegrati // Reconnect with xep198 conOne.connect().login(); - assertMessageWithBodyReceived(body2, collector); + assertMessageWithBodyReceived(body2, collector, conTwo.getUser()); send(body3, conOne, conTwo); - assertMessageWithBodyReceived(body3, collector); + assertMessageWithBodyReceived(body3, collector, conTwo.getUser()); } finally { collector.cancel(); @@ -84,9 +86,9 @@ public class StreamManagementTest extends AbstractSmackSpecificLowLevelIntegrati from.sendStanza(message); } - private static void assertMessageWithBodyReceived(String body, StanzaCollector collector) throws InterruptedException { + private static void assertMessageWithBodyReceived(String body, StanzaCollector collector, EntityFullJid recipient) throws InterruptedException { Message message = collector.nextResult(); - assertNotNull(message); - assertEquals(body, message.getBody()); + assertNotNull(message, "Expected '" + recipient + "' to receive a message stanza with body '" + body + "', but it didn't receive the message stanza at all."); + assertEquals(body, message.getBody(), "Expected '" + recipient + "'to receive a message stanza with a specific body, but it received a message stanza with a different body."); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/WaitForClosingStreamElementTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/WaitForClosingStreamElementTest.java index 2326866c7..dc9153aab 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smack/WaitForClosingStreamElementTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/WaitForClosingStreamElementTest.java @@ -39,6 +39,6 @@ public class WaitForClosingStreamElementTest extends AbstractSmackLowLevelIntegr Field closingStreamReceivedField = AbstractXMPPConnection.class.getDeclaredField("closingStreamReceived"); closingStreamReceivedField.setAccessible(true); boolean closingStreamReceived = (boolean) closingStreamReceivedField.get(connection); - assertTrue(closingStreamReceived); + assertTrue(closingStreamReceived, "Expected to, but did not, receive a closing stream element on connection " + connection); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/LowLevelRosterIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/LowLevelRosterIntegrationTest.java index 305e9ae47..9f150bd7a 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/LowLevelRosterIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/LowLevelRosterIntegrationTest.java @@ -49,7 +49,7 @@ public class LowLevelRosterIntegrationTest extends AbstractSmackLowLevelIntegrat final SimpleResultSyncPoint offlineTriggered = new SimpleResultSyncPoint(); - rosterOne.addPresenceEventListener(new AbstractPresenceEventListener() { + final AbstractPresenceEventListener presenceEventListener = new AbstractPresenceEventListener() { @Override public void presenceUnavailable(FullJid jid, Presence presence) { if (!jid.equals(conTwo.getUser())) { @@ -57,15 +57,24 @@ public class LowLevelRosterIntegrationTest extends AbstractSmackLowLevelIntegrat } offlineTriggered.signal(); } - }); + }; + rosterOne.addPresenceEventListener(presenceEventListener); - // Disconnect conTwo, this should cause an 'unavailable' presence to be send from conTwo to - // conOne. - conTwo.disconnect(); + try { + // Disconnect conTwo, this should cause an 'unavailable' presence to be sent from conTwo to + // conOne. + conTwo.disconnect(); - Boolean result = offlineTriggered.waitForResult(timeout); - if (!result) { - throw new Exception("presenceUnavailable() was not called"); + Boolean result = offlineTriggered.waitForResult(timeout); + if (!result) { + throw new Exception("presenceUnavailable() was not called"); + } + } finally { + // Clean up test fixture. + rosterOne.removePresenceEventListener(presenceEventListener); + conTwo.connect(); + conTwo.login(); + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java index 55aad5c12..98db355c6 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2020 Florian Schmaus + * Copyright 2015-2024 Florian Schmaus, 2022-2024 Guus der Kinderen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,23 +16,34 @@ */ package org.jivesoftware.smack.roster; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collection; import java.util.concurrent.TimeoutException; +import org.jivesoftware.smack.StanzaListener; +import org.jivesoftware.smack.filter.AndFilter; +import org.jivesoftware.smack.filter.FromMatchesFilter; +import org.jivesoftware.smack.filter.PresenceTypeFilter; +import org.jivesoftware.smack.filter.StanzaTypeFilter; import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.packet.PresenceBuilder; import org.jivesoftware.smack.roster.packet.RosterPacket.ItemType; +import org.jivesoftware.smack.util.Consumer; import org.jivesoftware.smack.util.StringUtils; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil; +import org.igniterealtime.smack.inttest.util.ResultSyncPoint; import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; import org.jxmpp.jid.BareJid; import org.jxmpp.jid.Jid; +@SpecificationReference(document = "RFC6121") public class RosterIntegrationTest extends AbstractSmackIntegrationTest { private final Roster rosterOne; @@ -61,7 +72,7 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest { final String conTwosRosterName = "ConTwo " + testRunId; final SimpleResultSyncPoint addedAndSubscribed = new SimpleResultSyncPoint(); - rosterOne.addRosterListener(new AbstractRosterListener() { + final RosterListener rosterListener = new AbstractRosterListener() { @Override public void entriesAdded(Collection addresses) { checkIfAddedAndSubscribed(addresses); @@ -78,16 +89,16 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest { BareJid bareJid = conTwo.getUser().asBareJid(); RosterEntry rosterEntry = rosterOne.getEntry(bareJid); if (rosterEntry == null) { - addedAndSubscribed.signalFailure("No roster entry for " + bareJid); + addedAndSubscribed.signalFailure("Added/Updated entry was not for " + bareJid); return; } String name = rosterEntry.getName(); if (StringUtils.isNullOrEmpty(name)) { - addedAndSubscribed.signalFailure("Roster entry without name"); + addedAndSubscribed.signalFailure("Added/Updated entry without name"); return; } if (!rosterEntry.getName().equals(conTwosRosterName)) { - addedAndSubscribed.signalFailure("Roster name does not match"); + addedAndSubscribed.signalFailure("Added/Updated entry name does not match. Expected: " + conTwosRosterName + " but was: " + rosterEntry.getName()); return; } if (!rosterEntry.getType().equals(ItemType.to)) { @@ -96,15 +107,327 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest { addedAndSubscribed.signal(); } } - }); + }; + + rosterOne.addRosterListener(rosterListener); try { rosterOne.createItemAndRequestSubscription(conTwo.getUser().asBareJid(), conTwosRosterName, null); - - assertTrue(addedAndSubscribed.waitForResult(2 * connection.getReplyTimeout())); + assertResult(addedAndSubscribed, + "A roster entry for " + conTwo.getUser().asBareJid() + " using the name '" + conTwosRosterName + + "' of type 'to' was expected to be added to the roster of " + conOne.getUser() + " (but it was not)."); } finally { rosterTwo.removeSubscribeListener(subscribeListener); + rosterOne.removeRosterListener(rosterListener); + } + } + + /** + * Asserts that when a user sends out a presence subscription request, the server sends a roster push back to the + * user. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "3.1.2", quote = + "After locally delivering or remotely routing the presence subscription request, the user's server MUST then " + + "send a roster push to all of the user's interested resources, containing the potential contact with a " + + "subscription state of \"none\" and with notation that the subscription is pending (via an 'ask' attribute " + + "whose value is \"subscribe\").") + public void testRosterPushAfterSubscriptionRequest() throws Exception { + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); + rosterTwo.setSubscriptionMode(Roster.SubscriptionMode.manual); // prevents a race condition when asserting the captured roster entry. + final ResultSyncPoint added = new ResultSyncPoint<>(); + + final RosterListener rosterListener = new AbstractRosterListener() { + @Override + public void entriesAdded(Collection addresses) { + for (Jid jid : addresses) { + if (!jid.equals(conTwo.getUser().asBareJid())) { + continue; + } + final BareJid bareJid = conTwo.getUser().asBareJid(); + RosterEntry rosterEntry = rosterOne.getEntry(bareJid); + added.signal(rosterEntry); + return; + } + } + }; + rosterOne.addRosterListener(rosterListener); + + final Presence subscribe = conOne.getStanzaFactory().buildPresenceStanza() + .ofType(Presence.Type.subscribe) + .to(conTwo.getUser().asBareJid()) + .build(); + + try { + conOne.sendStanza(subscribe); + + final RosterEntry rosterEntry = assertResult(added, "Expected the server to send a roster push back to '" + conOne.getUser() + "' after they sent a presence subscription request to '" + conTwo.getUser().asBareJid() + "' (but the server did not)."); + assertEquals(ItemType.none, rosterEntry.getType(), "Unexpected subscription type on roster push after '" + conOne.getUser() + "' sent a presence subscription request to '" + conTwo.getUser().asBareJid() + "'."); + assertTrue(rosterEntry.isSubscriptionPending(), "Missing 'ask=subscribe' attribute on roster push after '" + conOne.getUser() + "' sent a presence subscription request to '" + conTwo.getUser().asBareJid() + "'."); + } finally { + rosterTwo.setSubscriptionMode(Roster.getDefaultSubscriptionMode()); + rosterOne.removeRosterListener(rosterListener); + } + } + + /** + * Asserts that when a user sends out a presence subscription request to an entity for which the user does not have + * a pre-existing subscription, the server will deliver the subscription request to that entity. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "3.1.3", quote = + "if there is at least one available resource associated with the contact when the subscription request is " + + "received by the contact's server, then the contact's server MUST send that subscription request to all " + + "available resources in accordance with Section 8.") + public void testPresenceDeliveredToRecipient() throws Exception { + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); + + final ResultSyncPoint added = new ResultSyncPoint<>(); + final StanzaListener stanzaListener = stanza -> added.signal((Presence) stanza); + conTwo.addAsyncStanzaListener(stanzaListener, new AndFilter(StanzaTypeFilter.PRESENCE, FromMatchesFilter.createBare(conOne.getUser()))); + + final Presence subscribe = conOne.getStanzaFactory().buildPresenceStanza() + .ofType(Presence.Type.subscribe) + .to(conTwo.getUser().asBareJid()) + .build(); + + try { + conOne.sendStanza(subscribe); + final Presence received = assertResult(added, "Expected subscription request from '" + conOne.getUser() + "' to '" + conTwo.getUser().asBareJid() + "' to be delivered to " + conTwo.getUser() + " (but it did not)."); + assertEquals(Presence.Type.subscribe, received.getType(), "Unexpected presence type in presence stanza received by '" + conTwo.getUser() + "' after '" + conOne.getUser() + "' sent a presence subscription request."); + } finally { + conTwo.removeAsyncStanzaListener(stanzaListener); + } + } + + /** + * Asserts that when a user sends a presence subscription approval, the server stamps the bare JID of the sender, + * and delivers it to the requester. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "3.1.5", quote = + "When the contact's client sends the subscription approval, the contact's server MUST stamp the outbound " + + "stanza with the bare JID of the contact and locally deliver or remotely route the " + + "stanza to the user.") + public void testPresenceApprovalStampedAndDelivered() throws Exception { + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); + + rosterTwo.setSubscriptionMode(Roster.SubscriptionMode.accept_all); + + // Modify the outbound 'subscribed' stanza, to be 'wrong' (addressed to a full rather than a bare JID), to test if the server overrides this. + final Consumer interceptor = (PresenceBuilder presenceBuilder) -> presenceBuilder.to(conOne.getUser()).build(); + conTwo.addPresenceInterceptor(interceptor, p -> p.getType() == Presence.Type.subscribed); + + final ResultSyncPoint added = new ResultSyncPoint<>(); + final StanzaListener stanzaListener = stanza -> added.signal((Presence) stanza); + + conOne.addAsyncStanzaListener(stanzaListener, PresenceTypeFilter.SUBSCRIBED); + + final Presence subscribe = conOne.getStanzaFactory().buildPresenceStanza() + .ofType(Presence.Type.subscribe) + .to(conTwo.getUser().asBareJid()) + .build(); + + try { + conOne.sendStanza(subscribe); + + final Presence received = assertResult(added, "Expected presence 'subscribed' stanza to be delivered to '" + conOne.getUser() + "' after '" + conTwo.getUser() + "' approved their subscription request (but it was not)."); + assertEquals(conTwo.getUser().asBareJid(), received.getFrom().asEntityBareJidOrThrow(), "Expected presence 'subscribed' stanza that was delivered to '" + conOne.getUser() + "' after '" + conTwo.getUser() + "' approved their subscription request to have a 'from' attribute value that is associated to '" + conTwo.getUser().getLocalpart() + "' (but it did not)."); + assertTrue(received.getFrom().isEntityBareJid(), "Expected presence 'subscribed' stanza that was delivered to '" + conOne.getUser() + "' after '" + conTwo.getUser() + "' approved their subscription request to have a 'from' attribute value that is a bare JID (but it was not)."); + } finally { + rosterTwo.setSubscriptionMode(Roster.getDefaultSubscriptionMode()); + conTwo.removePresenceInterceptor(interceptor); + conOne.removeAsyncStanzaListener(stanzaListener); + } + } + + /** + * Asserts that when a user sends a presence subscription approval, the server sends a roster push to the user with + * a subscription 'from'. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "3.1.5", quote = + "The contact's server then MUST send an updated roster push to all of the contact's interested resources, " + + "with the 'subscription' attribute set to a value of \"from\".") + public void testPresenceApprovalYieldsRosterPush() throws Exception { + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); + + rosterTwo.setSubscriptionMode(Roster.SubscriptionMode.accept_all); + + final ResultSyncPoint updated = new ResultSyncPoint<>(); + + final RosterListener rosterListener = new AbstractRosterListener() { + @Override + public void entriesAdded(Collection addresses) { + for (Jid jid : addresses) { + if (!jid.equals(conOne.getUser().asBareJid())) { + continue; + } + BareJid bareJid = conOne.getUser().asBareJid(); + RosterEntry rosterEntry = rosterTwo.getEntry(bareJid); + updated.signal(rosterEntry); + } + } + }; + rosterTwo.addRosterListener(rosterListener); + + final Presence subscribe = conOne.getStanzaFactory().buildPresenceStanza() + .ofType(Presence.Type.subscribe) + .to(conTwo.getUser().asBareJid()) + .build(); + + try { + conOne.sendStanza(subscribe); + // The 'subscribe' gets automatically approved by conTwo. + + final RosterEntry entry = assertResult(updated, "Expected '" + conTwo.getUser() + "' to receive a roster push with an update for the entry of '" + conOne.getUser().asBareJid() + "' after '" + conTwo.getUser() + "' approved their subscription request."); + assertEquals(ItemType.from, entry.getType(), "Unexpected type for '" + conOne.getUser().asBareJid() + "''s entry in '" + conTwo.getUser().asBareJid() + "''s roster."); + } finally { + rosterTwo.setSubscriptionMode(Roster.getDefaultSubscriptionMode()); + rosterTwo.removeRosterListener(rosterListener); + } + } + + /** + * Asserts that when a user sends a presence subscription approval, the server sends a roster push to the user with + * a subscription 'both' when the contact already has a subscription to the other entity. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "3.1.5", quote = + "The contact's server then MUST send an updated roster push to all of the contact's interested resources, " + + "with the 'subscription' attribute set to a value of \"from\". (Here we assume that the contact does not " + + "already have a subscription to the user; if that were the case, the 'subscription' attribute would be set " + + "to a value of \"both\", as explained under Appendix A.)") + public void testPresenceApprovalYieldsRosterPush2() throws Exception { + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); + + // Setup fixture: establish one-way subscription. + rosterOne.setSubscriptionMode(Roster.SubscriptionMode.accept_all); + + final SimpleResultSyncPoint fixtureComplete = new SimpleResultSyncPoint(); + RosterListener rosterListenerTwo = new AbstractRosterListener() { + @Override + public void entriesAdded(Collection addresses) { + checkIfAdded(addresses); + } + @Override + public void entriesUpdated(Collection addresses) { + checkIfAdded(addresses); + } + private void checkIfAdded(Collection addresses) { + for (Jid jid : addresses) { + final BareJid bareJid = conOne.getUser().asBareJid(); + if (!jid.equals(bareJid)) { + continue; + } + if (rosterTwo.getEntry(bareJid) == null) { + continue; + } + if (rosterTwo.getEntry(bareJid).getType() == ItemType.none) { + continue; + } + fixtureComplete.signal(); + rosterTwo.removeRosterListener(this); + } + } + }; + rosterTwo.addRosterListener(rosterListenerTwo); + + final Presence subscribeOne = conTwo.getStanzaFactory().buildPresenceStanza() + .ofType(Presence.Type.subscribe) + .to(conOne.getUser().asBareJid()) + .build(); + try { + conTwo.sendStanza(subscribeOne); + + fixtureComplete.waitForResult(connection.getReplyTimeout()); + } finally { + rosterOne.setSubscriptionMode(Roster.getDefaultSubscriptionMode()); + rosterTwo.removeRosterListener(rosterListenerTwo); + } + + // Setup fixture is now complete. Execute the test. + rosterTwo.setSubscriptionMode(Roster.SubscriptionMode.accept_all); + + final ResultSyncPoint updated = new ResultSyncPoint<>(); + + rosterListenerTwo = new AbstractRosterListener() { + @Override + public void entriesUpdated(Collection addresses) { + for (Jid jid : addresses) { + if (!jid.equals(conOne.getUser().asBareJid())) { + continue; + } + BareJid bareJid = conOne.getUser().asBareJid(); + updated.signal(rosterTwo.getEntry(bareJid)); + } + } + }; + rosterTwo.addRosterListener(rosterListenerTwo); + + final Presence subscribeTwo = conOne.getStanzaFactory().buildPresenceStanza() + .ofType(Presence.Type.subscribe) + .to(conTwo.getUser().asBareJid()) + .build(); + + try { + conOne.sendStanza(subscribeTwo); + + final RosterEntry entry = assertResult(updated, "Expected '" + conTwo.getUser() + "' to receive a roster push with an update for the entry of '" + conOne.getUser().asBareJid() + "' after '" + conOne.getUser() + "' approved their subscription request."); + assertEquals(ItemType.both, entry.getType(), "Unexpected type for '" + conOne.getUser().asBareJid() + "''s entry in '" + conTwo.getUser().asBareJid() + "''s roster."); + } finally { + rosterTwo.setSubscriptionMode(Roster.getDefaultSubscriptionMode()); + rosterTwo.removeRosterListener(rosterListenerTwo); + } + } + + /** + * Asserts that when a presence subscription request is approved, the server sends the latest presence of the now + * subscribed entity to the subscriber. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "3.1.5", quote = + "The contact's server MUST then also send current presence to the user from each of the contact's available resources.") + public void testCurrentPresenceSentAfterSubscriptionApproval() throws Exception { + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); + + final String needle = "Look for me!"; + conTwo.sendStanza(conTwo.getStanzaFactory().buildPresenceStanza().setStatus(needle).build()); + + rosterTwo.setSubscriptionMode(Roster.SubscriptionMode.accept_all); + + final SimpleResultSyncPoint received = new SimpleResultSyncPoint(); + final StanzaListener stanzaListener = stanza -> { + final Presence presence = (Presence) stanza; + + String status = presence.getStatus(); + if (status == null) return; + + if (status.equals(needle)) { + received.signal(); + } + }; + conOne.addAsyncStanzaListener(stanzaListener, new AndFilter(StanzaTypeFilter.PRESENCE, FromMatchesFilter.createBare(conTwo.getUser()))); + + final Presence subscribe = conOne.getStanzaFactory().buildPresenceStanza() + .ofType(Presence.Type.subscribe) + .to(conTwo.getUser().asBareJid()) + .build(); + + try { + conOne.sendStanza(subscribe); + + assertResult(received, "Expected '" + conTwo.getUser() + "' to receive '" + conOne.getUser() + "''s current presence update (including the status '" + needle + "'), but it did not."); + } finally { + rosterTwo.setSubscriptionMode(Roster.getDefaultSubscriptionMode()); + conOne.removeAsyncStanzaListener(stanzaListener); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/caps/EntityCapsTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/caps/EntityCapsTest.java index 4c373a9ce..74d9aa6a8 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/caps/EntityCapsTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/caps/EntityCapsTest.java @@ -18,6 +18,7 @@ package org.jivesoftware.smackx.caps; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -51,7 +52,9 @@ import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.annotations.AfterClass; import org.igniterealtime.smack.inttest.annotations.BeforeClass; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; +@SpecificationReference(document = "XEP-0115", version = "1.6.0") public class EntityCapsTest extends AbstractSmackIntegrationTest { private final EntityCapsManager ecmTwo; @@ -94,7 +97,9 @@ public class EntityCapsTest extends AbstractSmackIntegrationTest { public void testLocalEntityCaps() throws InterruptedException, NoResponseException, XMPPErrorException, NotConnectedException { final String dummyFeature = getNewDummyFeature(); DiscoverInfo info = EntityCapsManager.getDiscoveryInfoByNodeVer(ecmTwo.getLocalNodeVer()); - assertFalse(info.containsFeature(dummyFeature)); + assertFalse(info.containsFeature(dummyFeature), + "Expected the service discovery info for node '" + ecmTwo.getLocalNodeVer() + + "' to contain the feature '" + dummyFeature + "' (but it did not)."); // TODO Shouldn't this assertion be in a unit test instead of an integration test? dropWholeEntityCapsCache(); @@ -118,8 +123,12 @@ public class EntityCapsTest extends AbstractSmackIntegrationTest { // The other connection has to receive this stanza and record the // information in order for this test to succeed. info = EntityCapsManager.getDiscoveryInfoByNodeVer(ecmTwo.getLocalNodeVer()); - assertNotNull(info); - assertTrue(info.containsFeature(dummyFeature)); + assertNotNull(info, + "Expected '" + conOne.getUser() + "' to have received an 'available' presence from '" + conTwo.getUser() + + "' with a new CAPS 'ver' attribute (but it did not)."); + assertTrue(info.containsFeature(dummyFeature), + "Expected the service discovery info for node '" + ecmTwo.getLocalNodeVer() + + "' to contain the feature '" + dummyFeature + "' (but it did not)."); // TODO As above: shouldn't this assertion be in a unit test instead of an integration test? } /** @@ -146,7 +155,7 @@ public class EntityCapsTest extends AbstractSmackIntegrationTest { // discover that DiscoverInfo info = sdmOne.discoverInfo(conTwo.getUser()); // that discovery should cause a disco#info - assertTrue(discoInfoSend.get()); + assertTrue(discoInfoSend.get(), "Expected '" + conOne.getUser() + "' to have made a disco/info request to '" + conTwo.getUser() + "', but it did not."); assertTrue(info.containsFeature(dummyFeature), "The info response '" + info + "' does not contain the expected feature '" + dummyFeature + '\''); discoInfoSend.set(false); @@ -154,8 +163,9 @@ public class EntityCapsTest extends AbstractSmackIntegrationTest { // discover that info = sdmOne.discoverInfo(conTwo.getUser()); // that discovery shouldn't cause a disco#info - assertFalse(discoInfoSend.get()); - assertTrue(info.containsFeature(dummyFeature)); + assertFalse(discoInfoSend.get(), "Expected '" + conOne.getUser() + "' to not have made a disco/info request to '" + conTwo.getUser() + "' (as CAPS should have been cached), but it did not."); + assertTrue(info.containsFeature(dummyFeature), + "The info response '" + info + "' does not contain the expected feature '" + dummyFeature + '\''); } @SmackIntegrationTest @@ -165,7 +175,8 @@ public class EntityCapsTest extends AbstractSmackIntegrationTest { addFeatureAndWaitForPresence(conOne, conTwo, dummyFeature); String nodeVerAfter = EntityCapsManager.getNodeVersionByJid(conTwo.getUser()); - assertFalse(nodeVerBefore.equals(nodeVerAfter)); + assertNotEquals(nodeVerBefore, nodeVerAfter, + "Expected the reported node 'ver' value to differ after a feature was added (but it did not)."); } @SmackIntegrationTest @@ -191,12 +202,12 @@ public class EntityCapsTest extends AbstractSmackIntegrationTest { DiscoverInfo info = sdmOne.discoverInfo(conTwo.getUser()); String u1ver = EntityCapsManager.getNodeVersionByJid(conTwo.getUser()); - assertNotNull(u1ver); + assertNotNull(u1ver, "Expected " + conOne.getUser() + " to have received a CAPS 'ver' value for " + conTwo.getUser() + " (but did not)."); DiscoverInfo entityInfo = EntityCapsManager.CAPS_CACHE.lookup(u1ver); - assertNotNull(entityInfo); + assertNotNull(entityInfo, "Expected the local static cache to have a value cached for 'ver' value '" + u1ver + "' (but it did not)."); - assertEquals(info.toXML().toString(), entityInfo.toXML().toString()); + assertEquals(info.toXML().toString(), entityInfo.toXML().toString(), "Expected the cached service/discovery info to be equal to the original (but it was not)."); } private static void dropWholeEntityCapsCache() { diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/chatstate/ChatStateIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/chatstate/ChatStateIntegrationTest.java index 11e1340f4..2c1b56e1a 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/chatstate/ChatStateIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/chatstate/ChatStateIntegrationTest.java @@ -27,8 +27,10 @@ import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.annotations.AfterClass; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; +@SpecificationReference(document = "XEP-0085", version = "2.1") public class ChatStateIntegrationTest extends AbstractSmackIntegrationTest { // Listener for composing chat state @@ -73,7 +75,7 @@ public class ChatStateIntegrationTest extends AbstractSmackIntegrationTest { Chat chat = ChatManager.getInstanceFor(conOne) .chatWith(conTwo.getUser().asEntityBareJid()); chat.send("Hi!"); - activeSyncPoint.waitForResult(timeout); + assertResult(activeSyncPoint, "Expected " + conTwo.getUser() + " to receive an 'active' chat state from " + conOne + " (but they did not)."); } @AfterClass diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandIntegrationTest.java index b4821f48e..1ece51611 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandIntegrationTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2023 Florian Schmaus + * Copyright 2023-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,9 @@ import org.jivesoftware.smackx.xdata.packet.DataForm; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; +@SpecificationReference(document = "XEP-0050", version = "1.3.0") public class AdHocCommandIntegrationTest extends AbstractSmackIntegrationTest { public AdHocCommandIntegrationTest(SmackIntegrationTestEnvironment environment) { @@ -71,7 +73,9 @@ public class AdHocCommandIntegrationTest extends AbstractSmackIntegrationTest { AdHocCommandData response = result.getResponse(); DataForm form = response.getForm(); FormField field = form.getField("my-field"); - assertNotNull(field); + assertNotNull(field, "Expected a field named 'my-field' to exist in the form that " + + conTwo.getUser() + " obtained from " + conOne.getUser() + "'s command node '" + commandNode + + "' (but it did not)."); } finally { manOne.unregisterCommand(commandNode); } @@ -93,7 +97,7 @@ public class AdHocCommandIntegrationTest extends AbstractSmackIntegrationTest { private static DataForm createDataFormOp() { FormField field = FormField.listSingleBuilder("op") - .setLabel("Arthimetic Operation") + .setLabel("Arithmetic Operation") .setRequired() .addOption("+") .addOption("-") @@ -118,7 +122,6 @@ public class AdHocCommandIntegrationTest extends AbstractSmackIntegrationTest { NextStage.nonFinal).build(); } - // TODO: Add API for every case where we return null or throw below. private static Integer extractIntegerField(SubmitForm form, String fieldName) throws XMPPErrorException { FormField field = form.getField(fieldName); if (field == null) @@ -258,7 +261,10 @@ public class AdHocCommandIntegrationTest extends AbstractSmackIntegrationTest { AdHocCommandResult.StatusCompleted completed = command.complete(submitForm).asCompletedOrThrow(); String operationResult = completed.getResponse().getForm().getField("result").getFirstValue(); - assertEquals("65", operationResult); + assertEquals("65", operationResult, + "Unexpected value in the field 'result' from the command result that " + conTwo.getUser() + + " received from " + conOne.getUser() + " after completing a multi-staged ad-hoc command on node '" + + commandNode + "'."); } finally { manTwo.unregisterCommand(commandNode); } @@ -316,7 +322,10 @@ public class AdHocCommandIntegrationTest extends AbstractSmackIntegrationTest { AdHocCommandResult.StatusCompleted completed = command.complete(submitForm).asCompletedOrThrow(); String operationResult = completed.getResponse().getForm().getField("result").getFirstValue(); - assertEquals("100", operationResult); + assertEquals("100", operationResult, + "Unexpected value in the field 'result' from the command result that " + conTwo.getUser() + + " received from " + conOne.getUser() + " after completing a multi-staged ad-hoc command on node '" + + commandNode + "'."); } finally { manTwo.unregisterCommand(commandNode); } @@ -345,7 +354,9 @@ public class AdHocCommandIntegrationTest extends AbstractSmackIntegrationTest { SubmitForm submitForm = form.getSubmitForm(); XMPPErrorException exception = assertThrows(XMPPErrorException.class, () -> command.next(submitForm)); - assertEquals(exception.getStanzaError().getCondition(), StanzaError.Condition.bad_request); + assertEquals(StanzaError.Condition.bad_request, exception.getStanzaError().getCondition(), + "Unexpected error condition received after " + conTwo.getUser() + " supplied an invalid argument " + + "to the command node '" + commandNode + "' of " + conOne.getUser()); } finally { manTwo.unregisterCommand(commandNode); } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferIntegrationTest.java index ac79e46ec..25ebb9b2a 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferIntegrationTest.java @@ -31,8 +31,10 @@ import org.jivesoftware.smackx.filetransfer.FileTransfer.Status; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; -import org.igniterealtime.smack.inttest.util.ResultSyncPoint; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; +import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; +@SpecificationReference(document = "XEP-0096", version = "1.3.1") public class FileTransferIntegrationTest extends AbstractSmackIntegrationTest { private static final int MAX_FT_DURATION = 360; @@ -65,7 +67,7 @@ public class FileTransferIntegrationTest extends AbstractSmackIntegrationTest { } private void genericfileTransferTest() throws Exception { - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + final SimpleResultSyncPoint resultSyncPoint = new SimpleResultSyncPoint(); final FileTransferListener receiveListener = new FileTransferListener() { @Override public void fileTransferRequest(FileTransferRequest request) { @@ -82,7 +84,7 @@ public class FileTransferIntegrationTest extends AbstractSmackIntegrationTest { os.flush(); dataReceived = os.toByteArray(); if (Arrays.equals(dataToSend, dataReceived)) { - resultSyncPoint.signal("Received data matches send data. \\o/"); + resultSyncPoint.signal(); } else { resultSyncPoint.signal(new Exception("Received data does not match")); @@ -115,7 +117,9 @@ public class FileTransferIntegrationTest extends AbstractSmackIntegrationTest { } } - resultSyncPoint.waitForResult(MAX_FT_DURATION * 1000); + assertResult(resultSyncPoint, MAX_FT_DURATION * 1000, + "Expected data to be transferred successfully from " + conOne.getUser() + " to " + conTwo.getUser() + + " (but it did not)."); ftManagerTwo.removeFileTransferListener(receiveListener); } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/geolocation/GeolocationIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/geolocation/GeolocationIntegrationTest.java index 7e149ff3b..1cd1d3b8d 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/geolocation/GeolocationIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/geolocation/GeolocationIntegrationTest.java @@ -17,7 +17,6 @@ package org.jivesoftware.smackx.geolocation; import java.net.URI; -import java.util.concurrent.TimeoutException; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; @@ -34,11 +33,12 @@ import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.annotations.AfterClass; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil; import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; -import org.junit.jupiter.api.Assertions; import org.jxmpp.util.XmppDateTime; +@SpecificationReference(document = "XEP-0080", version = "1.9") public class GeolocationIntegrationTest extends AbstractSmackIntegrationTest { private final GeoLocationManager glm1; @@ -106,16 +106,12 @@ public class GeolocationIntegrationTest extends AbstractSmackIntegrationTest { glm1.publishGeoLocation(data); // for the purpose of this test, this needs not be blocking/use publishAndWait(); // Wait for the data to be received. - try { - Object result = geoLocationReceived.waitForResult(timeout); - - // Explicitly assert the success case. - Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not."); - } catch (TimeoutException e) { - Assertions.fail("Expected to receive a PEP notification, but did not."); - } + assertResult(geoLocationReceived, + "Expected " + conTwo.getUser() + " to receive a PEP notification from " + conOne.getUser() + + " that contained '" + data.toXML() + "', but did not."); } finally { unregisterListener(glm2, geoLocationListener); + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); } } @@ -171,16 +167,12 @@ public class GeolocationIntegrationTest extends AbstractSmackIntegrationTest { registerListenerAndWait(glm2, ServiceDiscoveryManager.getInstanceFor(conTwo), geoLocationListener); // Wait for the data to be received. - try { - Object result = geoLocationReceived.waitForResult(timeout); - - // Explicitly assert the success case. - Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not."); - } catch (TimeoutException e) { - Assertions.fail("Expected to receive a PEP notification, but did not."); - } + assertResult(geoLocationReceived, + "Expected " + conTwo.getUser() + " to receive a PEP notification from " + conOne.getUser() + + " that contained '" + data.toXML() + "', but did not."); } finally { unregisterListener(glm2, geoLocationListener); + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java index 243bfec36..862a06af2 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java @@ -36,7 +36,9 @@ import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; +@SpecificationReference(document = "XEP-0363", version = "0.4.0") public class HttpFileUploadIntegrationTest extends AbstractSmackIntegrationTest { private static final int FILE_SIZE = 1024 * 128; @@ -80,7 +82,7 @@ public class HttpFileUploadIntegrationTest extends AbstractSmackIntegrationTest URL getUrl = hfumOne.uploadFile(file, new UploadProgressListener() { @Override public void onUploadProgress(long uploadedBytes, long totalBytes) { - double progress = uploadedBytes / totalBytes; + double progress = uploadedBytes / ((double) totalBytes); LOGGER.fine("HTTP File Upload progress " + progress + "% (" + uploadedBytes + '/' + totalBytes + ')'); } }); @@ -102,6 +104,6 @@ public class HttpFileUploadIntegrationTest extends AbstractSmackIntegrationTest byte[] downBytes = baos.toByteArray(); - assertArrayEquals(upBytes, downBytes); + assertArrayEquals(upBytes, downBytes, "Expected the downloaded bytes to be equal to the uploaded bytes (but they were not)."); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTControlIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTControlIntegrationTest.java index 498493b5b..4d4844adf 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTControlIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTControlIntegrationTest.java @@ -33,10 +33,12 @@ import org.jivesoftware.smackx.iot.control.element.SetData; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil; import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; import org.jxmpp.jid.Jid; +@SpecificationReference(document = "XEP-0347", version = "0.5.1") public class IoTControlIntegrationTest extends AbstractSmackIntegrationTest { private final IoTControlManager IoTControlManagerOne; @@ -87,7 +89,7 @@ public class IoTControlIntegrationTest extends AbstractSmackIntegrationTest { SetData data = new SetBoolData(testRunId, true); IoTSetResponse response = IoTControlManagerTwo.setUsingIq(conOne.getUser(), data); - assertNotNull(response); + assertNotNull(response, "Expected " + conOne.getUser() + " to receive an IQ response with an 'setResponse' child element, but no such response was received."); } finally { IoTControlManagerOne.uninstallThing(controlThing); diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDataIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDataIntegrationTest.java index 858eae9a7..731260475 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDataIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDataIntegrationTest.java @@ -37,8 +37,10 @@ import org.jivesoftware.smackx.iot.data.element.TimestampElement; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil; +@SpecificationReference(document = "XEP-0347", version = "0.5.1") public class IoTDataIntegrationTest extends AbstractSmackIntegrationTest { private final IoTDataManager iotDataManagerOne; @@ -84,23 +86,23 @@ public class IoTDataIntegrationTest extends AbstractSmackIntegrationTest { IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); } - assertEquals(1, values.size()); + assertEquals(1, values.size(), "An unexpected amount of momentary values was received by " + conOne.getUser()); IoTFieldsExtension iotFieldsExtension = values.get(0); List nodes = iotFieldsExtension.getNodes(); - assertEquals(1, nodes.size()); + assertEquals(1, nodes.size(), "The momentary value received by " + conOne.getUser() + " contains an unexpected amount of nodes."); NodeElement node = nodes.get(0); List timestamps = node.getTimestampElements(); - assertEquals(1, timestamps.size()); + assertEquals(1, timestamps.size(), "The node received by " + conOne.getUser() + " contains an unexpected amount of timestamps."); TimestampElement timestamp = timestamps.get(0); List fields = timestamp.getDataFields(); - assertEquals(1, fields.size()); + assertEquals(1, fields.size(), "The timestamp received by " + conOne.getUser() + " contains an unexpected amount of data fields."); IoTDataField dataField = fields.get(0); - assertTrue(dataField instanceof IoTDataField.IntField); + assertTrue(dataField instanceof IoTDataField.IntField, "The data field received by " + conOne.getUser() + " was expected to be an instance of " + IoTDataField.IntField.class.getSimpleName() + ", but instead, it was " + dataField.getClass().getSimpleName()); IoTDataField.IntField intDataField = (IoTDataField.IntField) dataField; - assertEquals(testRunId, intDataField.getName()); - assertEquals(value, intDataField.getValue()); + assertEquals(testRunId, intDataField.getName(), "Unexpected name in the data field received by " + conOne.getUser()); + assertEquals(value, intDataField.getValue(), "Unexpected value in the data field received by " + conOne.getUser()); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDiscoveryIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDiscoveryIntegrationTest.java index df6bf345e..7c3f3dcac 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDiscoveryIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDiscoveryIntegrationTest.java @@ -34,8 +34,10 @@ import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.jxmpp.jid.Jid; +@SpecificationReference(document = "XEP-0347", version = "0.5.1") public class IoTDiscoveryIntegrationTest extends AbstractSmackIntegrationTest { private final IoTDiscoveryManager discoveryManagerOne; @@ -60,7 +62,7 @@ public class IoTDiscoveryIntegrationTest extends AbstractSmackIntegrationTest { registerThing(discoveryManagerOne, thing); IoTClaimed iotClaimed = discoveryManagerTwo.claimThing(thing.getMetaTags()); - assertEquals(conOne.getUser().asBareJid(), iotClaimed.getJid()); + assertEquals(conOne.getUser().asBareJid(), iotClaimed.getJid(), "Thing claimed by an unexpected JID"); discoveryManagerTwo.disownThing(iotClaimed.getJid()); diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iqversion/VersionIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iqversion/VersionIntegrationTest.java index 9e903bbb8..05c8fa18d 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iqversion/VersionIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iqversion/VersionIntegrationTest.java @@ -28,7 +28,9 @@ import org.jivesoftware.smackx.iqversion.packet.Version; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; +@SpecificationReference(document = "XEP-0092", version = "1.1") public class VersionIntegrationTest extends AbstractSmackIntegrationTest { public VersionIntegrationTest(SmackIntegrationTestEnvironment environment) { @@ -45,8 +47,8 @@ public class VersionIntegrationTest extends AbstractSmackIntegrationTest { final String versionName = "Smack Integration Test " + testRunId; versionManagerTwo.setVersion(versionName, "1.0"); - assertTrue (versionManagerOne.isSupported(conTwo.getUser())); + assertTrue(versionManagerOne.isSupported(conTwo.getUser()), "Expected " + conTwo.getUser() + " to support " + Version.NAMESPACE + " (but it does not)."); Version version = versionManagerOne.getVersion(conTwo.getUser()); - assertEquals(versionName, version.getName()); + assertEquals(versionName, version.getName(), "Unexpected version name reported by " + conTwo.getUser()); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java index 046a33ac6..9b9cba7a3 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java @@ -42,9 +42,11 @@ import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; import org.jxmpp.jid.EntityBareJid; +@SpecificationReference(document = "XEP-0313", version = "0.6.3") public class MamIntegrationTest extends AbstractSmackIntegrationTest { private final MamManager mamManagerConTwo; @@ -113,14 +115,14 @@ public class MamIntegrationTest extends AbstractSmackIntegrationTest { .build(); MamQuery mamQuery = mamManagerConTwo.queryArchive(mamQueryArgs); - assertEquals(1, mamQuery.getMessages().size()); + assertEquals(1, mamQuery.getMessages().size(), conTwo.getUser() + " received an unexpected amount of messages in response to a MAM query."); Message mamMessage = mamQuery.getMessages().get(0); - assertEquals(messageId, mamMessage.getStanzaId()); - assertEquals(messageBody, mamMessage.getBody()); - assertEquals(conOne.getUser(), mamMessage.getFrom()); - assertEquals(userTwo, mamMessage.getTo()); + assertEquals(messageId, mamMessage.getStanzaId(), "The message received by " + conTwo.getUser() + " via a MAM query has an unexpected stanza ID."); + assertEquals(messageBody, mamMessage.getBody(), "The message received by " + conTwo.getUser() + " via a MAM query has an unexpected body."); + assertEquals(conOne.getUser(), mamMessage.getFrom(), "The message received by " + conTwo.getUser() + " via a MAM query has an unexpected from-attribute value."); + assertEquals(userTwo, mamMessage.getTo(), "The message received by " + conTwo.getUser() + " via a MAM query has an unexpected to-attribute value."); } @SmackIntegrationTest @@ -174,8 +176,8 @@ public class MamIntegrationTest extends AbstractSmackIntegrationTest { MamQuery mamQuery = mamManagerConTwo.queryArchive(mamQueryArgs); - assertFalse(mamQuery.isComplete()); - assertEquals(messagesPerPage, mamQuery.getMessageCount()); + assertFalse(mamQuery.isComplete(), "Expected the first MAM response received by " + conTwo.getUser() + " to NOT be complete (but it was)."); + assertEquals(messagesPerPage, mamQuery.getMessageCount(), "Unexpected message count in MAM response received by " + conTwo.getUser()); List> pages = new ArrayList<>(numPages); pages.add(mamQuery.getMessages()); @@ -185,12 +187,12 @@ public class MamIntegrationTest extends AbstractSmackIntegrationTest { boolean isLastQuery = additionalPageRequestNum == numPages - 2; if (isLastQuery) { - assertTrue(mamQuery.isComplete()); + assertTrue(mamQuery.isComplete(), "Expected the last MAM response received by " + conTwo.getUser() + " to be complete (but it was not)."); } else { - assertFalse(mamQuery.isComplete()); + assertFalse(mamQuery.isComplete(), "Expected an intermediate MAM response received by " + conTwo.getUser() + " to NOT be complete (but it was)."); } - assertEquals(messagesPerPage, page.size()); + assertEquals(messagesPerPage, page.size(), "Unexpected amount of messages in the MAM response page received by " + conTwo.getUser()); pages.add(page); } @@ -200,13 +202,13 @@ public class MamIntegrationTest extends AbstractSmackIntegrationTest { queriedMessages.addAll(messages); } - assertEquals(outgoingMessages.size(), queriedMessages.size()); + assertEquals(outgoingMessages.size(), queriedMessages.size(), "An unexpected total number of messages was received through MAM by " + conTwo.getUser()); for (int i = 0; i < outgoingMessages.size(); i++) { Message outgoingMessage = outgoingMessages.get(i); Message queriedMessage = queriedMessages.get(i); - assertEquals(outgoingMessage.getBody(), queriedMessage.getBody()); + assertEquals(outgoingMessage.getBody(), queriedMessage.getBody(), "Unexpected message body for message number " + (i + 1) + " as received by " + conTwo.getUser() + " (are messages received out of order?)"); } } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mood/MoodIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mood/MoodIntegrationTest.java index 1a83085dc..c430938db 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mood/MoodIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mood/MoodIntegrationTest.java @@ -16,8 +16,6 @@ */ package org.jivesoftware.smackx.mood; -import java.util.concurrent.TimeoutException; - import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; @@ -30,10 +28,11 @@ import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.annotations.AfterClass; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil; import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; -import org.junit.jupiter.api.Assertions; +@SpecificationReference(document = "XEP-0107", version = "1.2.1") public class MoodIntegrationTest extends AbstractSmackIntegrationTest { private final MoodManager mm1; @@ -80,13 +79,10 @@ public class MoodIntegrationTest extends AbstractSmackIntegrationTest { mm1.setMood(data); // for the purpose of this test, this needs not be blocking/use publishAndWait(); // Wait for the data to be received. - try { - moodReceived.waitForResult(timeout); - } catch (TimeoutException e) { - Assertions.fail("Expected to receive a PEP notification, but did not."); - } + assertResult(moodReceived, "Expected " + conTwo.getUser() + " to receive a PEP notification, but did not."); } finally { unregisterListener(mm2, moodListener); + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); } } @@ -119,16 +115,10 @@ public class MoodIntegrationTest extends AbstractSmackIntegrationTest { registerListenerAndWait(mm2, ServiceDiscoveryManager.getInstanceFor(conTwo), moodListener); // Wait for the data to be received. - try { - Object result = moodReceived.waitForResult(timeout); - - // Explicitly assert the success case. - Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not."); - } catch (TimeoutException e) { - Assertions.fail("Expected to receive a PEP notification, but did not."); - } + assertResult(moodReceived, "Expected " + conTwo.getUser() + " to receive a PEP notification, but did not."); } finally { unregisterListener(mm2, moodListener); + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java index 22316ed6e..9465ef14a 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/AbstractMultiUserChatIntegrationTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2021-2023 Florian Schmaus + * Copyright 2021-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,18 @@ package org.jivesoftware.smackx.muc; import java.util.List; +import java.util.logging.Level; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.XMPPException.XMPPErrorException; +import org.jivesoftware.smack.packet.StanzaError; import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smackx.muc.MultiUserChatException.MissingMucCreationAcknowledgeException; +import org.jivesoftware.smackx.muc.MultiUserChatException.MucAlreadyJoinedException; +import org.jivesoftware.smackx.muc.MultiUserChatException.NotAMucServiceException; +import org.jivesoftware.smackx.xdata.form.FillableForm; +import org.jivesoftware.smackx.xdata.form.Form; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; @@ -45,7 +53,7 @@ public abstract class AbstractMultiUserChatIntegrationTest extends AbstractSmack public AbstractMultiUserChatIntegrationTest(SmackIntegrationTestEnvironment environment) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, - InterruptedException, TestNotPossibleException { + InterruptedException, TestNotPossibleException, MucAlreadyJoinedException, MissingMucCreationAcknowledgeException, NotAMucServiceException, XmppStringprepException { super(environment); mucManagerOne = MultiUserChatManager.getInstanceFor(conOne); mucManagerTwo = MultiUserChatManager.getInstanceFor(conTwo); @@ -53,10 +61,40 @@ public abstract class AbstractMultiUserChatIntegrationTest extends AbstractSmack List services = mucManagerOne.getMucServiceDomains(); if (services.isEmpty()) { - throw new TestNotPossibleException("No MUC (XEP-45) service found"); + throw new TestNotPossibleException("No MUC (XEP-0045) service found"); } - mucService = services.get(0); + DomainBareJid needle = null; + for (final DomainBareJid service : services) { + MultiUserChat multiUserChat = null; + try { + String roomNameLocal = String.join("-", "smack-inttest-abstract", testRunId, StringUtils.insecureRandomString(6)); + EntityBareJid mucAddress = JidCreate.entityBareFrom(Localpart.from(roomNameLocal), service.getDomain()); + multiUserChat = mucManagerOne.getMultiUserChat(mucAddress); + + createMuc(multiUserChat, "test"); + + needle = service; + break; + } catch (XMPPException.XMPPErrorException e) { + mucCreationDisallowedOrThrow(e); + LOGGER.log(Level.FINER, "MUC service " + service + " does not allow MUC creation", e); + } finally { + tryDestroy(multiUserChat); + } + } + + if (needle == null) { + throw new TestNotPossibleException("No MUC (XEP-0045) service found that allows test users to createa new room. Considered MUC services: " + services); + } + mucService = needle; + } + + static void mucCreationDisallowedOrThrow(XMPPException.XMPPErrorException e) throws XMPPErrorException { + StanzaError.Condition condition = e.getStanzaError().getCondition(); + if (condition == StanzaError.Condition.not_allowed) + return; + throw e; } /** @@ -140,4 +178,58 @@ public abstract class AbstractMultiUserChatIntegrationTest extends AbstractSmack .makeHidden() .submitConfigurationForm(); } + + /** + * Creates a non-anonymous room. + * + *

    From XEP-0045 § 10.1.3:

    + *
    + * Note: The _whois configuration option specifies whether the room is non-anonymous (a value of "anyone"), + * semi-anonymous (a value of "moderators"), or fully anonymous (a value of "none", not shown here). + *
    + */ + static void createNonAnonymousMuc(MultiUserChat muc, Resourcepart resourceName) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException, MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException { + muc.create(resourceName); + Form configForm = muc.getConfigurationForm(); + FillableForm answerForm = configForm.getFillableForm(); + answerForm.setAnswer("muc#roomconfig_whois", "anyone"); + muc.sendConfigurationForm(answerForm); + } + + /** + * Creates a semi-anonymous room. + * + *

    From XEP-0045 § 10.1.3:

    + *
    + * Note: The _whois configuration option specifies whether the room is non-anonymous (a value of "anyone"), + * semi-anonymous (a value of "moderators"), or fully anonymous (a value of "none", not shown here). + *
    + */ + static void createSemiAnonymousMuc(MultiUserChat muc, Resourcepart resourceName) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException, MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException { + muc.create(resourceName); + Form configForm = muc.getConfigurationForm(); + FillableForm answerForm = configForm.getFillableForm(); + answerForm.setAnswer("muc#roomconfig_whois", "moderators"); + muc.sendConfigurationForm(answerForm); + } + + /** + * Creates a password-protected room. + */ + static void createPasswordProtectedMuc(MultiUserChat muc, Resourcepart resourceName, String password) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException, MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException { + muc.create(resourceName); + Form configForm = muc.getConfigurationForm(); + FillableForm answerForm = configForm.getFillableForm(); + answerForm.setAnswer("muc#roomconfig_passwordprotectedroom", true); + answerForm.setAnswer("muc#roomconfig_roomsecret", password); + muc.sendConfigurationForm(answerForm); + } + + static void setMaxUsers(MultiUserChat muc, int maxUsers) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException, SmackException.NotConnectedException { + Form configForm = muc.getConfigurationForm(); + FillableForm answerForm = configForm.getFillableForm(); + answerForm.setAnswer("muc#roomconfig_maxusers", maxUsers); + muc.sendConfigurationForm(answerForm); + } + } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java index 279160c0f..aa30d5b78 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatEntityIntegrationTest.java @@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.List; import java.util.Map; import org.jivesoftware.smack.SmackException; @@ -30,55 +31,71 @@ import org.jivesoftware.smack.packet.StanzaError; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.disco.packet.DiscoverItems; +import org.jivesoftware.smackx.muc.MultiUserChatException.MissingMucCreationAcknowledgeException; +import org.jivesoftware.smackx.muc.MultiUserChatException.MucAlreadyJoinedException; +import org.jivesoftware.smackx.muc.MultiUserChatException.NotAMucServiceException; +import org.jivesoftware.smackx.muc.packet.MUCInitialPresence; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; + +import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityFullJid; import org.jxmpp.jid.parts.Resourcepart; +import org.jxmpp.stringprep.XmppStringprepException; +@SpecificationReference(document = "XEP-0045", version = "1.34.6") public class MultiUserChatEntityIntegrationTest extends AbstractMultiUserChatIntegrationTest { public MultiUserChatEntityIntegrationTest(SmackIntegrationTestEnvironment environment) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, - SmackException.NotConnectedException, InterruptedException, TestNotPossibleException { + SmackException.NotConnectedException, InterruptedException, TestNotPossibleException, MucAlreadyJoinedException, MissingMucCreationAcknowledgeException, NotAMucServiceException, XmppStringprepException { super(environment); } /** - * Asserts that a MUC service can have its features discovered - * - *

    From XEP-0045 § 6.2:

    - *
    - * An entity may wish to discover if a service implements the Multi-User Chat protocol; in order to do so, it - * sends a service discovery information ("disco#info") query to the MUC service's JID. The service MUST return - * its identity and the features it supports. - *
    + * Asserts that a MUC service can be discovered. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "6.1", quote = + "An entity often discovers a MUC service by sending a Service Discovery items (\"disco#items\") request to " + + "its own server. The server then returns the services that are associated with it.") + public void mucTestForDiscoveringMuc() throws Exception { + // This repeats some logic from the `AbstractMultiUserChatIntegrationTest` constructor, but is preserved here + // as an explicit test, because that might not always be true. + List services = ServiceDiscoveryManager.getInstanceFor(conOne).findServices(MUCInitialPresence.NAMESPACE, true, false); + assertFalse(services.isEmpty(), "Expected to be able to find MUC services on the domain that '" + conOne.getUser() + "' is connecting to (but could not)."); + } + + /** + * Asserts that a MUC service can have its features discovered. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "6.2", quote = + "An entity may wish to discover if a service implements the Multi-User Chat protocol; in order to do so, it " + + "sends a service discovery information (\"disco#info\") query to the MUC service's JID. The service MUST " + + "return its identity and the features it supports.") public void mucTestForDiscoveringFeatures() throws Exception { - DiscoverInfo info = mucManagerOne.getMucServiceDiscoInfo(mucManagerOne.getMucServiceDomains().get(0)); - assertTrue(info.getIdentities().size() > 0); - assertTrue(info.getFeatures().size() > 0); + DiscoverInfo info = ServiceDiscoveryManager.getInstanceFor(conOne).discoverInfo(mucService); + assertFalse(info.getIdentities().isEmpty(), "Expected the service discovery information for service " + mucService + " to include identities (but it did not)."); + assertFalse(info.getFeatures().isEmpty(), "Expected the service discovery information for service " + mucService + " to include features (but it did not)."); } /** * Asserts that a MUC Service lists its public rooms. * - *

    From XEP-0045 § 6.3:

    - *
    - * The service discovery items ("disco#items") protocol enables an entity to query a service for a list of - * associated items, which in the case of a chat service would consist of the specific chat rooms hosted by the - * service. The service SHOULD return a full list of the public rooms it hosts (i.e., not return any rooms that - * are hidden). - *
    - * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "6.3", quote = + "The service discovery items (\"disco#items\") protocol enables an entity to query a service for a list of " + + "associated items, which in the case of a chat service would consist of the specific chat rooms hosted by the" + + "service. The service SHOULD return a full list of the public rooms it hosts (i.e., not return any rooms that" + + "are hidden).") public void mucTestForDiscoveringRooms() throws Exception { EntityBareJid mucAddressPublic = getRandomRoom("smack-inttest-publicroom"); MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddressPublic); @@ -97,22 +114,18 @@ public class MultiUserChatEntityIntegrationTest extends AbstractMultiUserChatInt tryDestroy(mucAsSeenByTwo); } - assertTrue(rooms.containsKey(mucAddressPublic)); - assertFalse(rooms.containsKey(mucAddressHidden)); + assertTrue(rooms.containsKey(mucAddressPublic), "Expected the disco response from " + mucService + " to include the public room " + mucAddressPublic + " (but it did not)."); + assertFalse(rooms.containsKey(mucAddressHidden), "Expected the disco response from " + mucService + " to not include the hidden room " + mucAddressHidden + " (but it did)."); } /** * Asserts that a MUC Service returns disco info for a room. * - *

    From XEP-0045 § 6.4:

    - *
    - * Using the disco#info protocol, an entity may also query a specific chat room for more detailed information - * about the room....The room MUST return its identity and SHOULD return the features it supports - *
    - * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "6.4", quote = + "Using the disco#info protocol, an entity may also query a specific chat room for more detailed information " + + "about the room....The room MUST return its identity and SHOULD return the features it supports") public void mucTestForDiscoveringRoomInfo() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest-discoinfo"); MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); @@ -126,23 +139,20 @@ public class MultiUserChatEntityIntegrationTest extends AbstractMultiUserChatInt tryDestroy(mucAsSeenByOne); } - assertTrue(discoInfo.getIdentities().size() > 0); - assertTrue(discoInfo.getFeatures().size() > 0); + assertFalse(discoInfo.getIdentities().isEmpty(), "Expected the service discovery information for room " + mucAddress + " to include identities (but it did not)."); + assertFalse(discoInfo.getFeatures().isEmpty(), "Expected the service discovery information for room " + mucAddress + " to include features (but it did not)."); + assertTrue(discoInfo.getFeatures().stream().anyMatch(feature -> MultiUserChatConstants.NAMESPACE.equals(feature.getVar())), "Expected the service discovery information for room " + mucAddress + " to include the '" + MultiUserChatConstants.NAMESPACE + "' feature (but it did not)."); } /** * Asserts that a MUC Service returns disco info for a room's items. * - *

    From XEP-0045 § 6.5:

    - *
    - * An entity MAY also query a specific chat room for its associated items. An implementation MAY return a list - * of existing occupants if that information is publicly available, or return no list at all if this information is - * kept private. - *
    - * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "6.5", quote = + "An entity MAY also query a specific chat room for its associated items. An implementation MAY return a list " + + "of existing occupants if that information is publicly available, or return no list at all if this " + + "information is kept private.") public void mucTestForDiscoveringRoomItems() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest-discoitems"); MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); @@ -155,22 +165,18 @@ public class MultiUserChatEntityIntegrationTest extends AbstractMultiUserChatInt tryDestroy(mucAsSeenByOne); } - assertEquals(1, roomItems.getItems().size()); + assertEquals(1, roomItems.getItems().size(), "Unexpected amount of disco items for " + mucAddress); } /** * Asserts that a non-occupant receives a Bad Request error when attempting to query an occupant by their * occupant JID. * - *

    From XEP-0045 § 6.6:

    - *
    - * If a non-occupant attempts to send a disco request to an address of the form <room@service/nick>, a MUC service - * MUST return a <bad-request/> error - *
    - * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "6.6", quote = + "If a non-occupant attempts to send a disco request to an address of the form , a MUC " + + "service MUST return a error") public void mucTestForRejectingDiscoOnRoomOccupantByNonOccupant() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest-discoitems"); MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); @@ -186,7 +192,8 @@ public class MultiUserChatEntityIntegrationTest extends AbstractMultiUserChatInt XMPPException.XMPPErrorException xe; try { xe = assertThrows(XMPPException.XMPPErrorException.class, - () -> ServiceDiscoveryManager.getInstanceFor(conTwo).discoverItems(mucAsSeenByOneUserJid)); + () -> ServiceDiscoveryManager.getInstanceFor(conTwo).discoverItems(mucAsSeenByOneUserJid), + "Expected an XMPP error when " + conTwo.getUser() + " was trying to discover items of " + mucAsSeenByOneUserJid); } finally { tryDestroy(mucAsSeenByOne); } @@ -200,6 +207,7 @@ public class MultiUserChatEntityIntegrationTest extends AbstractMultiUserChatInt expectedCondition = StanzaError.Condition.not_acceptable; break; } - assertEquals(xe.getStanzaError().getCondition(), expectedCondition); + assertEquals(expectedCondition, xe.getStanzaError().getCondition(), + "Unexpected error condition in error returned when " + conTwo.getUser() + " was trying to discover items of " + mucAsSeenByOneUserJid); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java index 36fa47815..be59229c9 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2020 Florian Schmaus + * Copyright 2015-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,100 +17,44 @@ package org.jivesoftware.smackx.muc; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; +import java.util.Set; import java.util.concurrent.TimeoutException; import org.jivesoftware.smack.MessageListener; import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.SmackException.NoResponseException; +import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.Presence; - -import org.jivesoftware.smackx.muc.packet.MUCUser; +import org.jivesoftware.smack.packet.StanzaError; +import org.jivesoftware.smackx.muc.MultiUserChatException.MissingMucCreationAcknowledgeException; +import org.jivesoftware.smackx.muc.MultiUserChatException.MucAlreadyJoinedException; +import org.jivesoftware.smackx.muc.MultiUserChatException.MucConfigurationNotSupportedException; +import org.jivesoftware.smackx.muc.MultiUserChatException.NotAMucServiceException; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; -import org.igniterealtime.smack.inttest.util.ResultSyncPoint; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.parts.Resourcepart; +import org.jxmpp.stringprep.XmppStringprepException; +@SpecificationReference(document = "XEP-0045", version = "1.34.6") public class MultiUserChatIntegrationTest extends AbstractMultiUserChatIntegrationTest { public MultiUserChatIntegrationTest(SmackIntegrationTestEnvironment environment) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, - InterruptedException, TestNotPossibleException { + InterruptedException, TestNotPossibleException, MucAlreadyJoinedException, MissingMucCreationAcknowledgeException, NotAMucServiceException, XmppStringprepException { super(environment); } - /** - * Asserts that when a user joins a room, they are themselves included on the list of users notified (self-presence). - * - *

    From XEP-0045 § 7.2.2:

    - *
    - * ...the service MUST also send presence from the new participant's occupant JID to the full JIDs of all the - * occupants (including the new occupant) - *
    - * - * @throws Exception when errors occur - */ - @SmackIntegrationTest - public void mucJoinTest() throws Exception { - EntityBareJid mucAddress = getRandomRoom("smack-inttest-join"); - - MultiUserChat muc = mucManagerOne.getMultiUserChat(mucAddress); - try { - Presence reflectedJoinPresence = muc.join(Resourcepart.from("nick-one")); - - MUCUser mucUser = MUCUser.from(reflectedJoinPresence); - - assertNotNull(mucUser); - assertTrue(mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)); - assertEquals(mucAddress + "/nick-one", reflectedJoinPresence.getFrom().toString()); - assertEquals(conOne.getUser().asEntityFullJidIfPossible().toString(), reflectedJoinPresence.getTo().toString()); - } finally { - tryDestroy(muc); - } - } - - /** - * Asserts that when a user leaves a room, they are themselves included on the list of users notified (self-presence). - * - *

    From XEP-0045 § 7.14:

    - *
    - * The service MUST then send a presence stanzas of type "unavailable" from the departing user's occupant JID to - * the departing occupant's full JIDs, including a status code of "110" to indicate that this notification is - * "self-presence" - *
    - * - * @throws Exception when errors occur - */ - @SmackIntegrationTest - public void mucLeaveTest() throws Exception { - EntityBareJid mucAddress = getRandomRoom("smack-inttest-leave"); - - MultiUserChat muc = mucManagerOne.getMultiUserChat(mucAddress); - try { - muc.join(Resourcepart.from("nick-one")); - - Presence reflectedLeavePresence = muc.leave(); - - MUCUser mucUser = MUCUser.from(reflectedLeavePresence); - assertNotNull(mucUser); - - assertTrue(mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)); - assertEquals(mucAddress + "/nick-one", reflectedLeavePresence.getFrom().toString()); - assertEquals(conOne.getUser().asEntityFullJidIfPossible().toString(), reflectedLeavePresence.getTo().toString()); - } finally { - muc.join(Resourcepart.from("nick-one")); // We need to be in the room to destroy the room - tryDestroy(muc); - } - } - @SmackIntegrationTest public void mucTest() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest-message"); @@ -119,14 +63,14 @@ public class MultiUserChatIntegrationTest extends AbstractMultiUserChatIntegrati MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); final String mucMessage = "Smack Integration Test MUC Test Message " + randomString; - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + final SimpleResultSyncPoint resultSyncPoint = new SimpleResultSyncPoint(); mucAsSeenByTwo.addMessageListener(new MessageListener() { @Override public void processMessage(Message message) { String body = message.getBody(); if (mucMessage.equals(body)) { - resultSyncPoint.signal(body); + resultSyncPoint.signal(); } } }); @@ -134,63 +78,197 @@ public class MultiUserChatIntegrationTest extends AbstractMultiUserChatIntegrati createMuc(mucAsSeenByOne, "one-" + randomString); mucAsSeenByTwo.join(Resourcepart.from("two-" + randomString)); mucAsSeenByOne.sendMessage(mucMessage); + try { - resultSyncPoint.waitForResult(timeout); - } catch (TimeoutException e) { - throw new AssertionError("Failed to receive presence", e); + assertResult(resultSyncPoint, "Expected " + conTwo.getUser() + " to receive message that was sent by " + conOne.getUser() + " in room " + mucAddress + " (but it did not)."); } finally { tryDestroy(mucAsSeenByOne); } } - /** - * Asserts that a user is notified when a room is destroyed - * - *

    From XEP-0045 § 10.9:

    - *
    - * A room owner MUST be able to destroy a room, especially if the room is persistent... The room removes all users from the room... and destroys the room - *
    + /** + * Asserts that an owner is notified of room destruction when they destroy a room. * * @throws TimeoutException when roomDestroyed event doesn't get fired * @throws Exception when other errors occur */ - @SmackIntegrationTest - public void mucDestroyTest() throws TimeoutException, Exception { + @SmackIntegrationTest(section = "10.9", quote = + "A room owner MUST be able to destroy a room, especially if the room is persistent... The room removes all " + + "users from the room... and destroys the room") + public void mucDestroyOwnerTest() throws TimeoutException, Exception { - EntityBareJid mucAddress = getRandomRoom("smack-inttest-destroy"); + EntityBareJid mucAddress = getRandomRoom("smack-inttest-destroy-owner"); MultiUserChat muc = mucManagerOne.getMultiUserChat(mucAddress); - muc.join(Resourcepart.from("nick-one")); + createMuc(muc, Resourcepart.from("one-" + randomString)); + + // These would be a test implementation bug, not assertion failure. + if (!mucManagerOne.getJoinedRooms().contains(mucAddress)) { + tryDestroy(muc); + throw new IllegalStateException("Expected user to have joined a room '" + mucAddress + "' (but does not appear to have done so)."); + } final SimpleResultSyncPoint mucDestroyed = new SimpleResultSyncPoint(); - @SuppressWarnings("deprecation") - DefaultUserStatusListener userStatusListener = new DefaultUserStatusListener() { + UserStatusListener userStatusListener = new UserStatusListener() { @Override - public void roomDestroyed(MultiUserChat alternateMUC, String reason) { + public void roomDestroyed(MultiUserChat alternateMUC, String password, String reason) { mucDestroyed.signal(); } }; muc.addUserStatusListener(userStatusListener); - assertEquals(1, mucManagerOne.getJoinedRooms().size()); - assertEquals(1, muc.getOccupantsCount()); - assertNotNull(muc.getNickname()); - try { muc.destroy("Dummy reason", null); - mucDestroyed.waitForResult(timeout); + assertResult(mucDestroyed, "Expected " + conOne.getUser() + " to be notified of destruction of room " + mucAddress + " (but was not)."); } finally { muc.removeUserStatusListener(userStatusListener); } - assertEquals(0, mucManagerOne.getJoinedRooms().size()); - assertEquals(0, muc.getOccupantsCount()); + Set joinedRooms = mucManagerOne.getJoinedRooms(); + assertFalse(muc.isJoined(), "Expected " + conOne.getUser() + " to no longer be in room " + mucAddress + " after it was destroyed, but it is still in."); + assertEquals(0, joinedRooms.size(), "Expected " + conOne.getUser() + " to no longer be in any rooms after " + mucAddress + " was destroyed. But it is still in " + joinedRooms); + assertEquals(0, muc.getOccupantsCount(), "Expected room " + mucAddress + " to no longer have any occupants after it was destroyed (but it has)."); assertNull(muc.getNickname()); } + /** + * Asserts that an occupant of a room is notified when a room is destroyed. + * + * @throws TimeoutException when roomDestroyed event doesn't get fired + * @throws Exception when other errors occur + */ + @SmackIntegrationTest(section = "10.9", quote = + "A room owner MUST be able to destroy a room, especially if the room is persistent... The room removes all " + + "users from the room... and destroys the room") + public void mucDestroyTestOccupant() throws TimeoutException, Exception { + + EntityBareJid mucAddress = getRandomRoom("smack-inttest-destroy-occupant"); + + MultiUserChat mucAsSeenByOwner = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByParticipant = mucManagerTwo.getMultiUserChat(mucAddress); + createMuc(mucAsSeenByOwner, Resourcepart.from("one-" + randomString)); + + // These would be a test implementation bug, not assertion failure. + mucAsSeenByParticipant.join(Resourcepart.from("two-" + randomString)); + if (!mucManagerTwo.getJoinedRooms().contains(mucAddress)) { + tryDestroy(mucAsSeenByOwner); + throw new IllegalStateException("Expected user to have joined a room '" + mucAddress + "' (but does not appear to have done so)."); + } + final SimpleResultSyncPoint mucDestroyed = new SimpleResultSyncPoint(); + + UserStatusListener userStatusListener = new UserStatusListener() { + @Override + public void roomDestroyed(MultiUserChat alternateMUC, String password, String reason) { + mucDestroyed.signal(); + } + }; + + mucAsSeenByParticipant.addUserStatusListener(userStatusListener); + + try { + mucAsSeenByOwner.destroy("Dummy reason", null); + assertResult(mucDestroyed, "Expected " + conTwo.getUser() + " to be notified of destruction of room " + mucAddress + " (but was not)."); + } finally { + mucAsSeenByParticipant.removeUserStatusListener(userStatusListener); + } + + Set joinedRooms = mucManagerTwo.getJoinedRooms(); + assertFalse(mucAsSeenByParticipant.isJoined(), "Expected " + conTwo.getUser() + " to no longer be in room " + mucAddress + " after it was destroyed, but it is still in."); + assertEquals(0, joinedRooms.size(), "Expected " + conTwo.getUser() + " to no longer be in any rooms after " + mucAddress + " was destroyed. But it is still in " + joinedRooms); + assertEquals(0, mucAsSeenByParticipant.getOccupantsCount(), "Expected room " + mucAddress + " to no longer have any occupants after it was destroyed (but it has)."); + assertNull(mucAsSeenByParticipant.getNickname()); + } + + @SmackIntegrationTest + public void mucNameChangeTest() + throws XmppStringprepException, MucAlreadyJoinedException, MissingMucCreationAcknowledgeException, + NotAMucServiceException, NoResponseException, XMPPErrorException, NotConnectedException, + InterruptedException, MucConfigurationNotSupportedException { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-muc-name-change"); + + MultiUserChat muc = mucManagerOne.getMultiUserChat(mucAddress); + createMuc(muc, Resourcepart.from("one-" + randomString)); + + final String newRoomName = "New Room Name (" + randomString + ")"; + + try { + muc.getConfigFormManager() + .setRoomName(newRoomName) + .submitConfigurationForm(); + + MultiUserChatManager mucManager = MultiUserChatManager.getInstanceFor(conTwo); + RoomInfo roomInfo = mucManager.getRoomInfo(muc.getRoom()); + assertEquals(newRoomName, roomInfo.getName()); + } finally { + tryDestroy(muc); + } + } + + @SmackIntegrationTest(section = "8.1", quote = "modify the subject [...] MUST be denied if the of the 'from' address of the request does not match " + + "the bare JID portion of one of the moderators; in this case, the service MUST return a error.") + public void mucTestVisitorNotAllowedToChangeSubject() throws XmppStringprepException, MucAlreadyJoinedException, + MissingMucCreationAcknowledgeException, NotAMucServiceException, NoResponseException, + XMPPErrorException, NotConnectedException, InterruptedException, TestNotPossibleException { + final EntityBareJid mucAddress = getRandomRoom("smack-inttest-visitor-change-subject"); + final MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + final MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + try { + MucConfigFormManager configFormManager = mucAsSeenByOne.getConfigFormManager(); + if (configFormManager.occupantsAreAllowedToChangeSubject()) { + configFormManager.disallowOccupantsToChangeSubject().submitConfigurationForm(); + } + + mucAsSeenByTwo.join(nicknameTwo); + + final XMPPException.XMPPErrorException e = assertThrows(XMPPException.XMPPErrorException.class, () -> { + mucAsSeenByTwo.changeSubject("Test Subject Change"); + }, "Expected an error after '" + conTwo.getUser() + + "' (that is not a moderator) tried to change the subject of room '" + mucAddress + + "' (but none occurred)."); + assertEquals(StanzaError.Condition.forbidden, e.getStanzaError().getCondition(), + "Unexpected error condition in the (expected) error that was returned to '" + + conTwo.getUser() + "' after it tried to change to subject of room '" + + mucAddress + "' while not being a moderator."); + } catch (MucConfigurationNotSupportedException e) { + throw new TestNotPossibleException(e); + } finally { + tryDestroy(mucAsSeenByOne); + } + } + + @SmackIntegrationTest + public void mucTestChangeRoomName() throws XmppStringprepException, MucAlreadyJoinedException, + MissingMucCreationAcknowledgeException, NotAMucServiceException, NoResponseException, + XMPPErrorException, NotConnectedException, InterruptedException, TestNotPossibleException { + final EntityBareJid mucAddress = getRandomRoom("smack-inttest-change-room-name"); + final MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + try { + String initialRoomName = "Initial Room Name"; + mucAsSeenByOne.getConfigFormManager().setRoomName(initialRoomName).submitConfigurationForm(); + RoomInfo roomInfo = mucManagerOne.getRoomInfo(mucAddress); + assertEquals(initialRoomName, roomInfo.getName()); + + String newRoomName = "New Room Name"; + mucAsSeenByOne.getConfigFormManager().setRoomName(newRoomName).submitConfigurationForm(); + roomInfo = mucManagerOne.getRoomInfo(mucAddress); + assertEquals(newRoomName, roomInfo.getName()); + } catch (MucConfigurationNotSupportedException e) { + throw new TestNotPossibleException(e); + } finally { + tryDestroy(mucAsSeenByOne); + } + } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatLowLevelIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatLowLevelIntegrationTest.java index e0f476306..e13384940 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatLowLevelIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatLowLevelIntegrationTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2020 Florian Schmaus + * Copyright 2015-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,13 +33,16 @@ import org.igniterealtime.smack.inttest.AbstractSmackLowLevelIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.parts.Localpart; import org.jxmpp.jid.parts.Resourcepart; +@SpecificationReference(document = "XEP-0048", version = "1.2") public class MultiUserChatLowLevelIntegrationTest extends AbstractSmackLowLevelIntegrationTest { + @SuppressWarnings("this-escape") public MultiUserChatLowLevelIntegrationTest(SmackIntegrationTestEnvironment environment) throws Exception { super(environment); AbstractXMPPConnection connection = getConnectedConnection(); @@ -66,7 +69,13 @@ public class MultiUserChatLowLevelIntegrationTest extends AbstractSmackLowLevelI final MultiUserChat muc = multiUserChatManager.getMultiUserChat(JidCreate.entityBareFrom( Localpart.from(randomMucName), mucComponent)); - MucCreateConfigFormHandle handle = muc.createOrJoin(mucNickname); + MucCreateConfigFormHandle handle; + try { + handle = muc.createOrJoin(mucNickname); + } catch (XMPPException.XMPPErrorException e) { + AbstractMultiUserChatIntegrationTest.mucCreationDisallowedOrThrow(e); + throw new TestNotPossibleException("MUC service " + mucComponent + " does not allow MUC creation", e); + } if (handle != null) { handle.makeInstant(); } @@ -83,7 +92,7 @@ public class MultiUserChatLowLevelIntegrationTest extends AbstractSmackLowLevelI // So we trigger it manually here. MucBookmarkAutojoinManager.getInstanceFor(connection).autojoinBookmarkedConferences(); - assertTrue(muc.isJoined()); + assertTrue(muc.isJoined(), "Expected " + connection.getUser() + " to automatically join room " + muc.getRoom() + " after a reconnect, but it did not."); // If the test went well, leave the MUC muc.leave(); diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatOccupantIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatOccupantIntegrationTest.java new file mode 100644 index 000000000..04ea0253c --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatOccupantIntegrationTest.java @@ -0,0 +1,1118 @@ +/** + * + * Copyright 2015-2024 Florian Schmaus, 2021 Dan Caseley + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.muc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; + +import org.jivesoftware.smack.PresenceListener; +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.StanzaListener; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.filter.FromMatchesFilter; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smack.packet.StanzaError; +import org.jivesoftware.smack.sm.predicates.ForEveryMessage; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smackx.muc.MultiUserChatException.MissingMucCreationAcknowledgeException; +import org.jivesoftware.smackx.muc.MultiUserChatException.MucAlreadyJoinedException; +import org.jivesoftware.smackx.muc.MultiUserChatException.MucConfigurationNotSupportedException; +import org.jivesoftware.smackx.muc.MultiUserChatException.NotAMucServiceException; +import org.jivesoftware.smackx.muc.packet.MUCItem; +import org.jivesoftware.smackx.muc.packet.MUCUser; + +import org.igniterealtime.smack.inttest.Configuration; +import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; +import org.igniterealtime.smack.inttest.TestNotPossibleException; +import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; +import org.igniterealtime.smack.inttest.util.MultiResultSyncPoint; +import org.igniterealtime.smack.inttest.util.ResultSyncPoint; +import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.jid.parts.Resourcepart; +import org.jxmpp.stringprep.XmppStringprepException; + +@SpecificationReference(document = "XEP-0045", version = "1.34.6") +public class MultiUserChatOccupantIntegrationTest extends AbstractMultiUserChatIntegrationTest { + + public MultiUserChatOccupantIntegrationTest(SmackIntegrationTestEnvironment environment) + throws SmackException.NoResponseException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, InterruptedException, TestNotPossibleException, MucAlreadyJoinedException, MissingMucCreationAcknowledgeException, NotAMucServiceException, XmppStringprepException { + super(environment); + } + + /** + * Asserts that when a user joins a room, all events are received, and in the correct order. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.1 & 7.2.2", quote = "" + + "§ 7.1 The order of events involved in joining a room needs to be consistent so that clients can know which events to expect when. After a client sends presence to join a room, the MUC service MUST send it events in the following order: 1. In-room presence from other occupants 2. In-room presence from the joining entity itself (so-called \"self-presence\") 3. Room history (if any) 4. The room subject [...]" + + "§ 7.2.2 This self-presence MUST NOT be sent to the new occupant until the room has sent the presence of all other occupants to the new occupant ... The service MUST first send the complete list of the existing occupants to the new occupant and only then send the new occupant's own presence to the new occupant") + public void mucJoinEventOrderingTest() throws Exception { + // This implementation deliberately does not use Smack's MUC API when trying to collect the order in which + // stanzas arrive. Instead, it joins a chatroom and listens for its stanzas using basic stanza handling. As + // this uses exactly one stanza listener, that's guaranteed to be invoked in order of stanza arrival, which is + // not necessarily the case when using the MUC API. + EntityBareJid mucAddress = getRandomRoom("smack-inttest-eventordering"); + final String mucSubject = "Subject smack-inttest-eventordering " + randomString; + final String mucMessage = "Message smack-inttest-eventordering " + randomString; + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + mucAsSeenByOne.changeSubject(mucSubject); // Blocks until confirmed. + + // Send and wait for the message to have been reflected, so that we can be sure it's part of the MUC history. + final SimpleResultSyncPoint messageReflectionSyncPoint = new SimpleResultSyncPoint(); + mucAsSeenByOne.addMessageListener(message -> { + String body = message.getBody(); + if (body == null) return; + + if (body.equals(mucMessage)) { + messageReflectionSyncPoint.signal(); + } + }); + + mucAsSeenByOne.sendMessage(mucMessage); + messageReflectionSyncPoint.waitForResult(timeout); + + final ResultSyncPoint subjectResultSyncPoint = new ResultSyncPoint<>(); + final List results = new ArrayList<>(); + final StanzaListener stanzaListener = stanza -> { + results.add(stanza); + if (stanza instanceof Message && ((Message) stanza).getSubject() != null) { + subjectResultSyncPoint.signal(((Message) stanza).getSubject()); + } + }; + conTwo.addStanzaListener(stanzaListener, FromMatchesFilter.create(mucAddress)); + + try { + mucAsSeenByTwo.join(nicknameTwo); + + subjectResultSyncPoint.waitForResult(timeout); // Wait for subject, as it should be 4th (last) + + assertEquals(4, results.size(), "Unexpected amount of stanzas received by '" + conTwo.getUser() + "' after it joined room '" + mucAddress + "'."); + assertTrue(results.get(0) instanceof Presence, "Expected the first stanza that was received by '" + conTwo.getUser() + "' after it joined room '" + mucAddress + "' to be a presence stanza (but it was not)."); + assertEquals(JidCreate.fullFrom(mucAddress, nicknameOne), results.get(0).getFrom(), "Unexpected 'from' address of the first stanza that was received by '" + conTwo.getUser() + "' after it joined room '" + mucAddress + "'."); + assertTrue(results.get(1) instanceof Presence, "Expected the second stanza that was received by '" + conTwo.getUser() + "' after it joined room '" + mucAddress + "' to be a presence stanza (but it was not)."); + assertEquals(JidCreate.fullFrom(mucAddress, nicknameTwo), results.get(1).getFrom(), "Unexpected 'from' address of the seconds stanza that was received by '" + conTwo.getUser() + "' after it joined room '" + mucAddress + "'."); + assertTrue(results.get(2) instanceof Message, "Expected the third stanza that was received by '" + conTwo.getUser() + "' after it joined room '" + mucAddress + "' to be a message stanza (but it was not)."); + assertEquals(mucMessage, ((Message) results.get(2)).getBody(), "The third stanza that was received by '" + conTwo.getUser() + "' after it joined room '" + mucAddress + "' was expected to be a different stanza."); + assertTrue(results.get(3) instanceof Message, "Expected the fourth stanza that was received by '" + conTwo.getUser() + "' after it joined room '" + mucAddress + "' to be a message stanza (but it was not)."); + assertEquals(mucSubject, ((Message) results.get(3)).getSubject(), "The fourth stanza that was received by '" + conTwo.getUser() + "' after it joined room '" + mucAddress + "' was expected to be a different stanza."); + } finally { + tryDestroy(mucAsSeenByOne); + conTwo.removeStanzaListener(stanzaListener); + } + } + + /** + * Asserts that when a user sends a message to a room without joining, they receive an error and the message is not + * sent to the occupants. + * + *

    From XEP-0045 § 7.2.1:

    + *
    + * In order to participate in the discussions held in a multi-user chat room, a user MUST first become an occupant + * by entering the room + *
    + * + *

    From XEP-0045 § 7.4:

    + *
    + * If the sender is not an occupant of the room, the service SHOULD return a <not-acceptable/> error to the + * sender and SHOULD NOT reflect the message to all occupants + *
    + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.1 & 7.4", quote = + "§ 7.2.1: In order to participate in the discussions held in a multi-user chat room, a user MUST first become an occupant by entering the room [...] " + + "§ 7.4: If the sender is not an occupant of the room, the service SHOULD return a error to the sender and SHOULD NOT reflect the message to all occupants") + public void mucSendBeforeJoiningTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-send-without-joining"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + createMuc(mucAsSeenByOne, Resourcepart.from("one-" + randomString)); + + ResultSyncPoint errorMessageResultSyncPoint = new ResultSyncPoint<>(); + conTwo.addStanzaListener(packet -> errorMessageResultSyncPoint.signal((Message) packet), ForEveryMessage.INSTANCE); + + ResultSyncPoint distributedMessageResultSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByOne.addMessageListener(distributedMessageResultSyncPoint::signal); + + try { + mucAsSeenByTwo.sendMessage("Message without Joining"); + Message response = assertResult(errorMessageResultSyncPoint, "Expected an error to be returned to '" + conTwo.getUser() + "' after it sent a message to room '" + mucAddress + "' without joining it first (but no error was returned)."); + assertEquals(StanzaError.Condition.not_acceptable, response.getError().getCondition(), "Unexpected error condition in the (expected) error that was returned to '" + conTwo.getUser() + "' after it sent a message to room '" + mucAddress + "' without joining it first."); + assertThrows(TimeoutException.class, () -> distributedMessageResultSyncPoint.waitForResult(1000), "Occupant '" + conOne.getUser() + "' should NOT have seen the message that was sent by '" + conTwo.getUser() + "' to room '" + mucAddress + "' without the sender have joined the room (but the message was observed by the occupant)."); + } finally { + tryDestroy(mucAsSeenByOne); + } + } + + /** + * Asserts that when a user joins a room, they are sent presence information about existing participants and + * themselves that includes role and affiliation information and appropriate status codes. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.2", quote = + "If the service is able to add the user to the room, it MUST send presence from all the existing participants' " + + "occupant JIDs to the new occupant's full JID, including extended presence information about roles in a " + + "single element qualified by the 'http://jabber.org/protocol/muc#user' namespace and containing an " + + " child with the 'role' attribute set to a value of \"moderator\", \"participant\", or \"visitor\", " + + "and with the 'affiliation' attribute set to a value of \"owner\", \"admin\", \"member\", or \"none\" as " + + "appropriate. [...] the \"self-presence\" sent by the room to the new user MUST include a status code of 110 " + + "so that the user knows this presence refers to itself as an occupant [...] The service MUST first send the " + + "complete list of the existing occupants to the new occupant and only then send the new occupant's own " + + "presence to the new occupant.") + public void mucJoinPresenceInformationTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-presenceinfo"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + SimpleResultSyncPoint oneSeesTwo = new SimpleResultSyncPoint(); + mucAsSeenByOne.addParticipantListener(presence -> { + if (nicknameTwo.equals(presence.getFrom().getResourceOrEmpty())) { + oneSeesTwo.signal(); + } + }); + mucAsSeenByTwo.join(nicknameTwo); + oneSeesTwo.waitForResult(timeout); + mucAsSeenByOne.grantModerator(nicknameTwo); + + List results = new ArrayList<>(); + mucAsSeenByThree.addParticipantListener(results::add); + + try { + // Will block until all self-presence is received, prior to which all others presences will have been received. + mucAsSeenByThree.join(nicknameThree); + + assertEquals(3, results.size(), "Unexpected amount of occupants seen by '" + conThree + "' in room '" + mucAddress + "' after joining."); // The 3rd will be self-presence. + assertNotNull(MUCUser.from(results.get(0)), "Expected to be able to parse a MUC occupant from '" + results.get(0) + "', but could not. Its syntax is likely incorrect."); // Smack implementation guarantees the "x" element and muc#user namespace + + // The order of all but the last presence (which should be the self-presence) is unpredictable. + MUCItem mucItemSelf = MUCUser.from(results.get(2)).getItem(); + Set others = new HashSet<>(); + others.add(MUCUser.from(results.get(0)).getItem()); + others.add(MUCUser.from(results.get(1)).getItem()); + + assertEquals(MUCAffiliation.none, mucItemSelf.getAffiliation(), "Unexpected MUC affiliation in reflected self-presence of '" + conThree.getUser() + "' joining room '" + mucAddress + "'."); + assertEquals(1, others.stream().filter(item -> MUCAffiliation.owner.equals(item.getAffiliation())).count(), "Unexpected amount of other occupants in room '" + mucAddress + "' (as observed by '" + conThree.getUser() + "') that have the 'owner' affiliation."); + assertEquals(1, others.stream().filter(item -> MUCAffiliation.none.equals(item.getAffiliation())).count(), "Unexpected amount of other occupants in room '" + mucAddress + "' (as observed by '" + conThree.getUser() + "') that have no affiliation."); + + assertEquals(MUCRole.participant, mucItemSelf.getRole(), "Unexpected MUC role in reflected self-presence of '" + conThree.getUser() + "' joining room '" + mucAddress + "'."); + assertEquals(2, others.stream().filter(item -> MUCRole.moderator.equals(item.getRole())).count(), "Unexpected amount of other occupants in room '" + mucAddress + "' (as observed by '" + conThree.getUser() + "') that have the 'moderator' role."); + + assertTrue(MUCUser.from(results.get(2)).getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110), "Expected to find status '" + MUCUser.Status.PRESENCE_TO_SELF_110 + "' in reflected self-presence of '" + conThree.getUser() + "' joining room '" + mucAddress + "' (but did not)."); + } finally { + tryDestroy(mucAsSeenByOne); + } + } + + /** + * Asserts that when a user joins a room, all users are sent presence information about the new participant. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.2", quote = + "the service MUST also send presence from the new participant's occupant JID to the full JIDs of all the " + + "occupants (including the new occupant)") + public void mucJoinPresenceBroadcastTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-presenceinfo"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + mucAsSeenByTwo.join(nicknameTwo); + + final MultiResultSyncPoint syncPoint = new MultiResultSyncPoint<>(2); + + mucAsSeenByOne.addParticipantListener(presence -> { + if (nicknameThree.equals(presence.getFrom().getResourceOrEmpty())) { + syncPoint.signal(presence); + } + }); + + mucAsSeenByTwo.addParticipantListener(presence -> { + if (nicknameThree.equals(presence.getFrom().getResourceOrEmpty())) { + syncPoint.signal(presence); + } + }); + + try { + mucAsSeenByThree.join(nicknameThree); + + List results = assertResult(syncPoint, "Expected all occupants of room '" + mucAddress + "' to be notified of '" + conThree.getUser() + "' using nickname '" + nicknameThree + "' joining the room (but one or more did not get notified)"); + assertTrue(results.stream().allMatch( + result -> JidCreate.fullFrom(mucAddress, nicknameThree).equals(result.getFrom())), + "Expected all occupants of room '" + mucAddress + "' to be notified of '" + conThree.getUser() + "' using nickname '" + nicknameThree + "' joining the room (but one or more got notified for a different user)."); + assertTrue(results.stream().anyMatch( + result -> result.getTo().equals(conOne.getUser().asEntityFullJidIfPossible())), + "Expected '" + conOne.getUser().asEntityFullJidIfPossible() + "' to be notified of '" + conThree.getUser() + "' joining room '" + mucAddress + "' (but did not)"); + assertTrue(results.stream().anyMatch( + result -> result.getTo().equals(conTwo.getUser().asEntityFullJidIfPossible())), + "Expected '" + conTwo.getUser().asEntityFullJidIfPossible() + "' to be notified of '" + conThree.getUser() + "' joining room '" + mucAddress + "' (but did not)"); + } finally { + tryDestroy(mucAsSeenByOne); + } + } + + /** + * Asserts that when a user enters a non-anonymous room, the presence notifications contain extended presence + * information. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.3", quote = + "If the room is non-anonymous, the service MUST send the new occupant's full JID to all occupants using " + + "extended presence information in an element qualified by the 'http://jabber.org/protocol/muc#user' " + + "namespace and containing an child with a 'jid' attribute specifying the occupant's full JID. [...]" + + "If the user is entering a room that is non-anonymous (i.e., which informs all occupants of each occupant's " + + "full JID as shown above), the service MUST warn the user by including a status code of \"100\" in the " + + "initial presence that the room sends to the new occupant.") + public void mucJoinNonAnonymousRoomTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-joinnonanonymousroom"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + + createNonAnonymousMuc(mucAsSeenByOne, nicknameOne); + + final ResultSyncPoint participantOneSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByOne.addParticipantListener(presence -> { + if (nicknameTwo.equals(presence.getFrom().getResourceOrEmpty())) { + participantOneSyncPoint.signal(presence); + } + }); + + final ResultSyncPoint participantTwoSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByTwo.addParticipantListener(presence -> { + if (nicknameTwo.equals(presence.getFrom().getResourceOrEmpty())) { + participantTwoSyncPoint.signal(presence); + } + }); + + try { + mucAsSeenByTwo.join(nicknameTwo); + Presence presenceReceivedByOne = assertResult(participantOneSyncPoint, "Expected '" + conOne.getUser() + "' to receive a presence stanza after '" + conTwo.getUser() + "' joined room '" + mucAddress + "' (but did not)."); + Presence presenceReceivedByTwo = assertResult(participantTwoSyncPoint, "Expected '" + conTwo.getUser() + "' to receive a presence stanza after they themselves joined room '" + mucAddress + "' (but did not)."); + + // Check the presence received by participant one for inclusion of full jid of participant two + MUCUser announcedParticipantTwoUser = MUCUser.from(presenceReceivedByOne); + assertNotNull(announcedParticipantTwoUser, "Expected to be able to parse a MUC occupant from '" + presenceReceivedByOne + "', but could not. Its syntax is likely incorrect."); // Smack implementation guarantees the "x" element and muc#user namespace + assertNotNull(announcedParticipantTwoUser.getItem(), "Expected to be able to parse a MUC occupant item from '" + presenceReceivedByOne + "', but could not. Its syntax is likely incorrect."); + assertEquals(conTwo.getUser().asEntityFullJidOrThrow(), announcedParticipantTwoUser.getItem().getJid(), "Expected extended presence information received by '" + conOne.getUser() + "' after '" + conTwo.getUser() + "' joined room '" + mucAddress + "' to include their full JID."); + + // Check the presence received by participant two for inclusion of status 100 + assertTrue(MUCUser.from(presenceReceivedByTwo).getStatus().stream().anyMatch(status -> 100 == status.getCode()), + "Expected to find status '100' in reflected self-presence of '" + conTwo.getUser() + "' joining room '" + mucAddress + "' (but did not)."); + + } finally { + tryDestroy(mucAsSeenByOne); + } + } + + /** + * Asserts that when a user enters a semi-anonymous room, the presence notifications received by occupants that + * are not a moderator does not contain extended presence information. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.4", quote = + "If the room is semi-anonymous, the service MUST send presence from the new occupant to all occupants as " + + "specified above (i.e., unless the room is configured to not broadcast presence from new occupants below a " + + "certain affiliation level as controlled by the \"muc#roomconfig_presencebroadcast\" room configuration " + + "option), but MUST include the new occupant's full JID only in the presence notifications it sends to " + + "occupants with a role of \"moderator\" and not to non-moderator occupants.") + public void mucJoinSemiAnonymousRoomReceivedByNonModeratorTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-seminanonymous-by-non-moderator"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + + createSemiAnonymousMuc(mucAsSeenByOne, nicknameOne); + + mucAsSeenByTwo.join(nicknameTwo); + + // First pass: participant two is not a moderator yet + final ResultSyncPoint participantTwoSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByTwo.addParticipantListener(presence -> { + if (nicknameThree.equals(presence.getFrom().getResourceOrEmpty())) { + participantTwoSyncPoint.signal(presence); + } + }); + + try { + mucAsSeenByThree.join(nicknameThree); + Presence presenceReceivedByTwo = assertResult(participantTwoSyncPoint, "Expected '" + conTwo.getUser() + "' to receive presence when '" + conThree.getUser() + "' joined room '" + mucAddress + "' (but did not)."); + + // Check the presence received by participant two for exclusion of full jid of participant three + assertNull(MUCUser.from(presenceReceivedByTwo).getItem().getJid(), "Did not expect '" + conTwo.getUser() + "' (who is not a moderator at this stage) to receive the full JID of '" + conThree.getUser() + "' when they joined room '" + mucAddress + "' (but they did)."); + } finally { + tryDestroy(mucAsSeenByOne); + } + } + + /** + * Asserts that when a user enters a semi-anonymous room, the presence notifications contain extended presence + * information when sent to moderators. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.4", quote = + "If the room is semi-anonymous, the service MUST send presence from the new occupant to all occupants as " + + "specified above (i.e., unless the room is configured to not broadcast presence from new occupants below a " + + "certain affiliation level as controlled by the \"muc#roomconfig_presencebroadcast\" room configuration " + + "option), but MUST include the new occupant's full JID only in the presence notifications it sends to " + + "occupants with a role of \"moderator\" and not to non-moderator occupants.") + public void mucJoinSemiAnonymousRoomReceivedByModeratorTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-seminanonymous-by-moderator"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + + // Second pass: participant two is now a moderator + createSemiAnonymousMuc(mucAsSeenByOne, nicknameOne); + mucAsSeenByTwo.join(nicknameTwo); + + mucAsSeenByOne.grantModerator(nicknameTwo); + final ResultSyncPoint participantTwoSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByTwo.addParticipantListener(presence -> { + if (nicknameThree.equals(presence.getFrom().getResourceOrEmpty())) { + participantTwoSyncPoint.signal(presence); + } + }); + + try { + mucAsSeenByThree.join(nicknameThree); + Presence presenceReceivedByTwo = assertResult(participantTwoSyncPoint, "Expected '" + conTwo.getUser() + "' to receive presence when '" + conThree.getUser() + "' joined room '" + mucAddress + "' (but did not)."); + + // Check the presence received by participant two for inclusion of full jid of participant three + MUCUser announcedParticipantThreeUser = MUCUser.from(presenceReceivedByTwo); + assertEquals(conThree.getUser().asEntityFullJidOrThrow(), announcedParticipantThreeUser.getItem().getJid(), "Expected '" + conTwo.getUser() + "' (who is a moderator at this stage) to receive the full JID of '" + conThree.getUser() + "' when they joined room '" + mucAddress + "' (but they did not)."); + } finally { + tryDestroy(mucAsSeenByOne); + } + } + + /** + * Asserts that a user can not enter a password-protected room without supplying a password. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.5", quote = + "If the room requires a password and the user did not supply one (or the password provided is incorrect), the " + + "service MUST deny access to the room and inform the user that they are unauthorized; this is done by returning " + + "a presence stanza of type \"error\" specifying a error.") + public void mucJoinPasswordProtectedWithoutPasswordRoomTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-enterpasswordprotectedroom"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + + final String correctPassword = StringUtils.insecureRandomString(8); + + createPasswordProtectedMuc(mucAsSeenByOne, nicknameOne, correctPassword); + + try { + // First try: no password + XMPPException.XMPPErrorException noPasswordErrorException = assertThrows( + XMPPException.XMPPErrorException.class, () -> mucAsSeenByTwo.join(nicknameTwo), + "Expected an error to be returned when '" + conTwo.getUser() + "' attempted to join password-protected room '" + mucAddress + "' without a password."); + assertNotNull(noPasswordErrorException.getStanzaError(), + "Expected an error to be returned when '" + conTwo.getUser() + "' attempted to join password-protected room '" + mucAddress + "' without a password."); + assertEquals(StanzaError.Condition.not_authorized, noPasswordErrorException.getStanzaError().getCondition(), + "Unexpected condition in the (expected) error that was returned when '" + conTwo.getUser() + "' attempted to join password-protected room '" + mucAddress + "' without a password."); + } finally { + mucAsSeenByOne.destroy(); + } + } + + /** + * Asserts that a user can not enter a password-protected room without supplying the correct password. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.5", quote = + "If the room requires a password and the user did not supply one (or the password provided is incorrect), the " + + "service MUST deny access to the room and inform the user that they are unauthorized; this is done by returning " + + "a presence stanza of type \"error\" specifying a error.") + public void mucJoinPasswordProtectedRoomWrongPasswordTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-enterpasswordprotectedroom"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + + final String correctPassword = StringUtils.insecureRandomString(8); + + createPasswordProtectedMuc(mucAsSeenByOne, nicknameOne, correctPassword); + + try { + // Second try: wrong password + XMPPException.XMPPErrorException wrongPasswordErrorException = assertThrows( + XMPPException.XMPPErrorException.class, + () -> mucAsSeenByTwo.join(nicknameTwo, correctPassword + "_"), + "Expected an error to be returned when '" + conTwo.getUser() + "' attempted to join password-protected room '" + mucAddress + "' using an incorrect password."); + assertNotNull(wrongPasswordErrorException.getStanzaError(), + "Expected an error to be returned when '" + conTwo.getUser() + "' attempted to join password-protected room '" + mucAddress + "' using an incorrect password."); + assertEquals(StanzaError.Condition.not_authorized, wrongPasswordErrorException.getStanzaError().getCondition(), + "Unexpected condition in the (expected) error that was returned when '" + conTwo.getUser() + "' attempted to join password-protected room '" + mucAddress + "' using an incorrect password."); + } finally { + mucAsSeenByOne.destroy(); + } + } + + /** + * Asserts that a user can enter a password-protected room when supplying the correct password. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.5", quote = + "If the room requires a password and the user did not supply one (or the password provided is incorrect), the " + + "service MUST deny access to the room and inform the user that they are unauthorized; this is done by returning " + + "a presence stanza of type \"error\" specifying a error.") + public void mucJoinPasswordProtectedRoomCorrectPasswordTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-enterpasswordprotectedroom"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + + final String correctPassword = StringUtils.insecureRandomString(8); + + createPasswordProtectedMuc(mucAsSeenByOne, nicknameOne, correctPassword); + + // Set up to receive presence responses on successful join + final ResultSyncPoint participantTwoSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByTwo.addParticipantListener(presence -> { + if (nicknameTwo.equals(presence.getFrom().getResourceOrEmpty())) { + participantTwoSyncPoint.signal(presence); + } + }); + + try { + // Third try: correct password + mucAsSeenByTwo.join(nicknameTwo, correctPassword); + Presence presenceCorrectPassword = assertResult(participantTwoSyncPoint, "Expected '" + conTwo.getUser() + "' to be able to join password-protected room '" + mucAddress + "' using the correct password (but no join-presence was received)."); + assertNull(presenceCorrectPassword.getError(), "Unexpected error in join-presence of '" + conTwo.getUser() + "' after joining password-protected room '" + mucAddress + "' using the correct password: " + presenceCorrectPassword.getError()); + } finally { + mucAsSeenByOne.destroy(); + } + } + + /** + * Asserts that a user can not enter a members-only room while not being a member. + * + * This test does not cover § 9.3, aka the happy path. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.6", quote = + "If the room is members-only but the user is not on the member list, the service MUST deny access to the " + + "room and inform the user that they are not allowed to enter the room; this is done by returning a presence " + + "stanza of type \"error\" specifying a error condition.") + public void mucJoinMembersOnlyRoomTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-entermembersonlyroom"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + + createMembersOnlyMuc(mucAsSeenByOne, nicknameOne); + + try { + XMPPException.XMPPErrorException registrationRequiredErrorException = assertThrows( + XMPPException.XMPPErrorException.class, () -> mucAsSeenByTwo.join(nicknameTwo), + "Expected an error to be returned when non-member '" + conTwo.getUser() + "' tries to join member-online room '" + mucAddress + "' (but an error was not returned)."); + assertNotNull(registrationRequiredErrorException, "Expected an error to be returned when non-member '" + conTwo.getUser() + "' tries to join member-online room '" + mucAddress + "' (but an error was not returned)."); + assertNotNull(registrationRequiredErrorException.getStanzaError(), "Expected an error to be returned when non-member '" + conTwo.getUser() + "' tries to join member-online room '" + mucAddress + "' (but an error was not returned)."); + assertEquals(StanzaError.Condition.registration_required, registrationRequiredErrorException.getStanzaError().getCondition(), + "Unexpected condition in the (expected) error that was returned when non-member '" + conTwo.getUser() + "' joined member-online room '" + mucAddress + "' (."); + } finally { + mucAsSeenByOne.destroy(); + } + } + + /** + * Asserts that a user can not enter a room while being banned. + * + *

    From XEP-0045 § 7.2.7:

    + *
    + * + *
    + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.7", quote = + "If the user has been banned from the room (i.e., has an affiliation of \"outcast\"), the service MUST deny " + + "access to the room and inform the user of the fact that they are banned; this is done by returning a presence " + + "stanza of type \"error\" specifying a error condition.") + public void mucBannedUserJoinRoomTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-banneduser"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + + mucAsSeenByOne.banUser(conTwo.getUser().asBareJid(), "Insufficient witchcraft"); + + try { + XMPPException.XMPPErrorException forbiddenErrorException = assertThrows( + XMPPException.XMPPErrorException.class, () -> mucAsSeenByTwo.join(nicknameTwo), + "Expected an error to be returned when outcast '" + conTwo.getUser() + "' tries to join room '" + mucAddress + "' (but an error was not returned)."); + assertNotNull(forbiddenErrorException, "Expected an error to be returned when outcast '" + conTwo.getUser() + "' tries to join room '" + mucAddress + "' (but an error was not returned)."); + assertNotNull(forbiddenErrorException.getStanzaError(), "Expected an error to be returned when outcast '" + conTwo.getUser() + "' tries to join room '" + mucAddress + "' (but an error was not returned)."); + assertEquals(StanzaError.Condition.forbidden, forbiddenErrorException.getStanzaError().getCondition(), + "Unexpected condition in the (expected) error to was returned when outcast '" + conTwo.getUser() + "' tried to join room '" + mucAddress + "'."); + } finally { + mucAsSeenByOne.destroy(); + } + } + + /** + * Asserts that a user can not enter a room with the same nickname as another user who is already present. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.8", quote = + "If the room already contains another user with the nickname desired by the user seeking to enter the room " + + "(or if the nickname is reserved by another user on the member list), the service MUST deny access to the " + + "room and inform the user of the conflict; this is done by returning a presence stanza of type \"error\" " + + "specifying a error condition.") + public void mucNicknameConflictJoinRoomTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-nicknameclash"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + + try { + XMPPException.XMPPErrorException conflictErrorException = assertThrows( + XMPPException.XMPPErrorException.class, () -> mucAsSeenByTwo.join(nicknameOne), + "Expected an error to be returned when '" + conTwo.getUser() + "' tried to join room '" + mucAddress + "' using the nickname '" + nicknameOne + "' that was already in used by another occupant of the room (but an error was not returned)."); + assertNotNull(conflictErrorException, "Expected an error to be returned when '" + conTwo.getUser() + "' tried to join room '" + mucAddress + "' using the nickname '" + nicknameOne + "' that was already in used by another occupant of the room (but an error was not returned)."); + assertNotNull(conflictErrorException.getStanzaError(), "Expected an error to be returned when '" + conTwo.getUser() + "' tried to join room '" + mucAddress + "' using the nickname '" + nicknameOne + "' that was already in used by another occupant of the room (but an error was not returned)."); + assertEquals(StanzaError.Condition.conflict, conflictErrorException.getStanzaError().getCondition(), + "Unexpected condition in the (expected) error that was returned when '" + conTwo.getUser() + "' tried to join room '" + mucAddress + "' using the nickname '" + nicknameOne + "' that was already in used by another occupant of the room."); + } finally { + mucAsSeenByOne.destroy(); + } + } + + /** + * Asserts that a room can not go past the configured maximum number of users, if a non-admin non-owner user + * attempts to join. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.9", quote = + "If the room has reached its maximum number of occupants, the service SHOULD deny access to the room and " + + "inform the user of the restriction; this is done by returning a presence stanza of type \"error\" " + + "specifying a error condition. Alternatively, the room could kick an \"idle user\" " + + "in order to free up space (where the definition of \"idle user\" is up to the implementation).") + public void mucMaxUsersLimitJoinRoomTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-maxusersreached"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + setMaxUsers(mucAsSeenByOne, 2); + + // Set up for participant 1 to receive presence responses on join of participant 2 + final ResultSyncPoint participantOneSeesTwoSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByOne.addParticipantListener(presence -> { + if (nicknameTwo.equals(presence.getFrom().getResourceOrEmpty())) { + participantOneSeesTwoSyncPoint.signal(presence); + } + }); + + // Set up for participant 3 to receive presence responses on join of participant 3 + final ResultSyncPoint participantThreeSeesThreeSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByThree.addParticipantListener(presence -> { + if (nicknameThree.equals(presence.getFrom().getResourceOrEmpty())) { + participantThreeSeesThreeSyncPoint.signal(presence); + } + }); + + try { + mucAsSeenByTwo.join(nicknameTwo); + participantOneSeesTwoSyncPoint.waitForResult(timeout); + + assertEquals(2, mucAsSeenByOne.getOccupantsCount(), "Unexpected occupant count as seen by '" + conOne.getUser() + "' in room '" + mucAddress + "' (prior to the join attempt of a third occupant)."); + + // Now user 3 may or may not be able to join the room. The service can either deny access to user three, or + // it can kick user 2. Both strategies would comply with the specification. So the only thing we can + // reasonably test here is whether the room doesn't have more occupants than its max size. + + XMPPException.XMPPErrorException errorException = assertThrows(XMPPException.XMPPErrorException.class, () -> mucAsSeenByThree.join(nicknameThree)); + + final StanzaError.Condition expectedCondition; + switch (sinttestConfiguration.compatibilityMode) { + default: + expectedCondition = StanzaError.Condition.service_unavailable; + break; + case ejabberd: + expectedCondition = StanzaError.Condition.resource_constraint; + break; + } + + StanzaError stanzaError = errorException.getStanzaError(); + assertNotNull(stanzaError); + + assertEquals(expectedCondition, stanzaError.getCondition()); + + // Now we should wait until participant one is informed about the (probably failed) new participant three + // room join. But if joining failed, there will be no such update. All we can reasonably do is wait until + // participant three has received its own presence response. This is not watertight though. + participantThreeSeesThreeSyncPoint.waitForResult(timeout); + + // Irrespective of the way the implementation handles max users, there should still be only 2 users. + assertEquals(2, mucAsSeenByOne.getOccupantsCount(), "Unexpected occupant count as seen by '" + conOne.getUser() + "' in room '" + mucAddress + "' (after the join attempt of a third occupant)."); + + } finally { + mucAsSeenByOne.destroy(); + } + } + + /** + * Asserts that an admin can still join a room that has reached the configured maximum number of users. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.9", quote = + "If the room has reached its maximum number of occupants and a room admin or owner attempts to join, the " + + "room MUST allow the admin or owner to join, up to some reasonable number of additional occupants; this " + + "helps to prevent denial of service attacks caused by stuffing the room with non-admin users.") + public void mucMaxUsersLimitAdminCanStillJoinRoomTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-maxusersreached-adminjoin"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + setMaxUsers(mucAsSeenByOne, 2); + + // Set up for participant 1 to receive presence responses on join of participant 2 + // Set up for participant 1 to receive presence responses on join of participant 3 + final ResultSyncPoint participantOneSeesTwoSyncPoint = new ResultSyncPoint<>(); + final ResultSyncPoint participantOneSeesThreeSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByOne.addParticipantListener(presence -> { + if (nicknameTwo.equals(presence.getFrom().getResourceOrEmpty())) { + participantOneSeesTwoSyncPoint.signal(presence); + } else if (nicknameThree.equals(presence.getFrom().getResourceOrEmpty())) { + participantOneSeesThreeSyncPoint.signal(presence); + } + }); + + try { + mucAsSeenByTwo.join(nicknameTwo); + participantOneSeesTwoSyncPoint.waitForResult(timeout); + + assertEquals(2, mucAsSeenByOne.getOccupantsCount(), "Unexpected occupant count in room '" + mucAddress + "' (as observed by '" + conOne.getUser() + "') (prior to an admin attempting to join a room that was already having its maximum number of occupants)."); + + mucAsSeenByOne.grantAdmin(conThree.getUser().asBareJid()); // blocking call + mucAsSeenByThree.join(nicknameThree); + participantOneSeesThreeSyncPoint.waitForResult(timeout); + + assertNotNull(mucAsSeenByOne.getOccupant(JidCreate.entityFullFrom(mucAddress, nicknameThree)), "Expected admin '" + conThree.getUser() + "' to be in room '" + mucAddress + "' (as observed by '" + conOne.getUser() + "'), but was not."); + assertEquals(3, mucAsSeenByOne.getOccupantsCount(), "Unexpected occupant count in room '" + mucAddress + "' (as observed by '" + conOne.getUser() + "') (after an admin joined a room that was already having its maximum number of occupants)"); + } finally { + mucAsSeenByOne.destroy(); + } + } + + /** + * Asserts that an owner can still join a room that has reached the configured maximum number of users. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.9", quote = + "If the room has reached its maximum number of occupants and a room admin or owner attempts to join, the " + + "room MUST allow the admin or owner to join, up to some reasonable number of additional occupants; this " + + "helps to prevent denial of service attacks caused by stuffing the room with non-admin users.") + public void mucMaxUsersLimitOwnerCanStillJoinRoomTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-maxusersreached-ownerjoin"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + setMaxUsers(mucAsSeenByOne, 2); + + // Set up for participant 1 to receive presence responses on join of participant 2 + // Set up for participant 1 to receive presence responses on join of participant 3 + final ResultSyncPoint participantOneSeesTwoSyncPoint = new ResultSyncPoint<>(); + final ResultSyncPoint participantOneSeesThreeSyncPoint = new ResultSyncPoint<>(); + mucAsSeenByOne.addParticipantListener(presence -> { + if (nicknameTwo.equals(presence.getFrom().getResourceOrEmpty())) { + participantOneSeesTwoSyncPoint.signal(presence); + } else if (nicknameThree.equals(presence.getFrom().getResourceOrEmpty())) { + participantOneSeesThreeSyncPoint.signal(presence); + } + }); + + try { + mucAsSeenByTwo.join(nicknameTwo); + participantOneSeesTwoSyncPoint.waitForResult(timeout); + + assertEquals(2, mucAsSeenByOne.getOccupantsCount(), "Unexpected occupant count in room '" + mucAddress + "' (as observed by '" + conOne.getUser() + "') (prior to an owner attempting to join a room that was already having its maximum number of occupants)."); + + mucAsSeenByOne.grantOwnership(conThree.getUser().asBareJid()); // blocking call + mucAsSeenByThree.join(nicknameThree); + participantOneSeesThreeSyncPoint.waitForResult(timeout); + + assertNotNull(mucAsSeenByOne.getOccupant(JidCreate.entityFullFrom(mucAddress, nicknameThree)), "Expected owner '" + conThree.getUser() + "' to be in room '" + mucAddress + "' (as observed by '" + conOne.getUser() + "'), but was not."); + assertEquals(3, mucAsSeenByOne.getOccupantsCount(), "Unexpected occupant count in room '" + mucAddress + "' (as observed by '" + conOne.getUser() + "') (after an owner joined a room that was already having its maximum number of occupants)"); + + } finally { + mucAsSeenByOne.destroy(); + } + } + + /** + * Asserts that a room can not be entered while it still being created (locked). + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.10", quote = + "If a user attempts to enter a room while it is \"locked\" (i.e., before the room creator provides an " + + "initial configuration and therefore before the room officially exists), the service MUST refuse entry and " + + "return an error to the user.") + public void mucJoinLockedRoomTest() throws Exception { + if (sinttestConfiguration.compatibilityMode == Configuration.CompatibilityMode.ejabberd) { + throw new TestNotPossibleException("ejabberd does not implement MUC locked rooms as per XEP-0045 § 7.2.10"); + } + + EntityBareJid mucAddress = getRandomRoom("smack-inttest-lockedroom"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + + // Note the absence of handle.makeInstant() here. The room is still being created at this point, until a + // configuration is set. + mucAsSeenByOne.create(nicknameOne); + + try { + XMPPException.XMPPErrorException conflictErrorException = assertThrows( + XMPPException.XMPPErrorException.class, () -> mucAsSeenByTwo.join(nicknameTwo), + "Expected an error to be returned when '" + conTwo.getUser() + "' tried to join room '" + mucAddress + "' that is still being created/is locked (but no error was returned)."); + assertNotNull(conflictErrorException, "Expected an error to be returned when '" + conTwo.getUser() + "' tried to join room '" + mucAddress + "' that is still being created/is locked (but no error was returned)."); + assertNotNull(conflictErrorException.getStanzaError(), "Expected an error to be returned when '" + conTwo.getUser() + "' tried to join room '" + mucAddress + "' that is still being created/is locked (but no error was returned)."); + assertEquals(StanzaError.Condition.item_not_found, conflictErrorException.getStanzaError().getCondition(), + "Unexpected condition in the (expected) error that was returned when '" + conTwo.getUser() + "' tried to join room '" + mucAddress + "' that is still being created/is locked."); + + } finally { + mucAsSeenByOne.destroy(); + } + } + + /** + * Asserts that a user is warned when entering a room that allows public logging. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.2.12", quote = + "If the user is entering a room in which the discussions are logged to a public archive (often accessible " + + "via HTTP), the service SHOULD allow the user to enter the room but MUST also warn the user that the " + + "discussions are logged. This is done by including a status code of \"170\" in the initial presence that the " + + "room sends to the new occupant.") + public void mucJoinRoomWithPublicLoggingTest() throws Exception { + final EntityBareJid mucAddress = getRandomRoom("smack-inttest-publiclogging"); + + final MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + final MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + + try { + mucAsSeenByOne.getConfigFormManager() + .enablePublicLogging() + .submitConfigurationForm(); + + Presence twoPresence = mucAsSeenByTwo.join(nicknameTwo); + assertTrue(MUCUser.from(twoPresence).getStatus().contains(MUCUser.Status.create(170)), + "Expected initial presence reflected to '" + conTwo.getUser() + "' when joining room '" + mucAddress + "' to include the status code '170' (but it did not)."); + } catch (MucConfigurationNotSupportedException e) { + throw new TestNotPossibleException(e); + } finally { + mucAsSeenByOne.destroy(); + } + } + + /** + * Asserts that all users in a room are correctly informed about nickname change of a participant. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.6", quote = + "A common feature of chat rooms is the ability for an occupant to change his or her nickname within the room. " + + "In MUC this is done by sending updated presence information to the room, specifically by sending presence to " + + "a new occupant JID in the same room (changing only the resource identifier in the occupant JID). The service " + + "then sends two presence stanzas to the full JID of each occupant (including the occupant who is changing his " + + "or her room nickname), one of type \"unavailable\" for the old nickname and one indicating availability for " + + "the new nickname. The unavailable presence MUST contain the following as extended presence information in an " + + "; element qualified by the 'http://jabber.org/protocol/muc#user' namespace: - The new nickname (in this " + + "case, nick='oldhag') - A status code of 303 This enables the recipients to correlate the old roomnick with " + + "the new roomnick.\n") + public void mucChangeNicknameInformationTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-changenickname"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwoOriginal = Resourcepart.from("two-original-" + randomString); + final Resourcepart nicknameTwoNew = Resourcepart.from("two-new-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + + try { + SimpleResultSyncPoint participantOneSeesTwoEnter = new SimpleResultSyncPoint(); + mucAsSeenByOne.addParticipantListener(presence -> { + if (nicknameTwoOriginal.equals(presence.getFrom().getResourceOrEmpty())) { + participantOneSeesTwoEnter.signal(); + } + }); + + // Have participant two enter the room + mucAsSeenByTwo.join(nicknameTwoOriginal); + participantOneSeesTwoEnter.waitForResult(timeout); + + // Although logic dictates that the 'unavailable' presence stanzas for the old nick should precede the presence + // stanza for the new nick - the specification does not dictate that. So we should allow for the order to be + // reversed. Here we will expect an unavailable and an available presence stanza sent to both participant one + // and participant two. So that adds up to a total of four. + MultiResultSyncPoint participantTwoPresencesSyncPoint = new MultiResultSyncPoint<>(4); + PresenceListener mucPresenceListener = presence -> { + Resourcepart fromResource = presence.getFrom().getResourceOrEmpty(); + if (nicknameTwoOriginal.equals(fromResource) || nicknameTwoNew.equals(fromResource)) { + participantTwoPresencesSyncPoint.signal(presence); + } + }; + mucAsSeenByOne.addParticipantListener(mucPresenceListener); + mucAsSeenByTwo.addParticipantListener(mucPresenceListener); + + // Participant two changes nickname + mucAsSeenByTwo.changeNickname(nicknameTwoNew); + final List partTwoPresencesReceived = participantTwoPresencesSyncPoint.waitForResults( + timeout); + + final List unavailablePresencesReceivedByOne = partTwoPresencesReceived.stream().filter( + presence -> !presence.isAvailable()).filter( + presence -> presence.getTo().equals(conOne.getUser().asEntityFullJidIfPossible())).collect( + Collectors.toList()); + final List unavailablePresencesReceivedByTwo = partTwoPresencesReceived.stream().filter( + presence -> !presence.isAvailable()).filter( + presence -> presence.getTo().equals(conTwo.getUser().asEntityFullJidIfPossible())).collect( + Collectors.toList()); + final List availablePresencesReceivedByOne = partTwoPresencesReceived.stream().filter( + Presence::isAvailable).filter( + presence -> presence.getTo().equals(conOne.getUser().asEntityFullJidIfPossible())).collect( + Collectors.toList()); + final List availablePresencesReceivedByTwo = partTwoPresencesReceived.stream().filter( + Presence::isAvailable).filter( + presence -> presence.getTo().equals(conTwo.getUser().asEntityFullJidIfPossible())).collect( + Collectors.toList()); + + // Validate that both users received both 'available' and 'unavailable' presence stanzas + assertEquals(1, unavailablePresencesReceivedByOne.size(), "Expected '" + conOne.getUser() + "' to have received one 'unavailable' presence when '" + conTwo.getUser() + "' changed its nickname in room '" + mucAddress + "' (but did not)"); + assertEquals(1, unavailablePresencesReceivedByTwo.size(), "Expected '" + conTwo.getUser() + "' to have received one 'unavailable' presence when they changed their nickname in room '" + mucAddress + "' (but did not)"); + assertEquals(1, availablePresencesReceivedByOne.size(), "Expected '" + conOne.getUser() + "' to have received one 'available' presence when '" + conTwo.getUser() + "' changed its nickname in room '" + mucAddress + "' (but did not)"); + assertEquals(1, availablePresencesReceivedByTwo.size(), "Expected '" + conTwo.getUser() + "' to have received one 'available' presence when they changed their nickname in room '" + mucAddress + "' (but did not)"); + + // Validate that the received 'unavailable' presence stanzas contain the status and items elements as specified + assertTrue(MUCUser.from(unavailablePresencesReceivedByOne.get(0)).getStatus().stream().anyMatch(status -> 303 == status.getCode()), + "Expected the 'unavailable' presence of user '" + conTwo.getUser() + "' reflecting their nickname change in room '" + mucAddress + "' as received by '" + conOne.getUser() + "' to include status code '303' (but it did not)."); + assertEquals(nicknameTwoNew, MUCUser.from(unavailablePresencesReceivedByOne.get(0)).getItem().getNick(), + "Expected the 'unavailable' presence of user '" + conTwo.getUser() + "' reflecting their nickname change in room '" + mucAddress + "' as received by '" + conOne.getUser() + "' to include the new nickname (but it did not)."); + assertTrue(MUCUser.from(unavailablePresencesReceivedByTwo.get(0)).getStatus().stream().anyMatch(status -> 303 == status.getCode()), + "Expected the 'unavailable' presence of user '" + conTwo.getUser() + "' reflecting their nickname change in room '" + mucAddress + "' as received by themselves to include status code '303' (but it did not)."); + assertEquals(nicknameTwoNew, MUCUser.from(unavailablePresencesReceivedByTwo.get(0)).getItem().getNick(), + "Expected the 'unavailable' presence of user '" + conTwo.getUser() + "' reflecting their nickname change in room '" + mucAddress + "' as received by themselves to include the new nickname (but it did not)."); + + // Validate that the received 'available' presence stanzas have the new nickname as from + assertEquals(nicknameTwoNew, availablePresencesReceivedByOne.get(0).getFrom().getResourceOrEmpty(), "Expected the 'available' presence of '" + conTwo.getUser() + "' as received by '" + conOne.getUser() + "' in room '" + mucAddress + "' to be sent 'from' their new nickname (but it was not)."); + assertEquals(nicknameTwoNew, availablePresencesReceivedByTwo.get(0).getFrom().getResourceOrEmpty(), "Expected the 'available' presence of '" + conTwo.getUser() + "' as received by themselves in room '" + mucAddress + "' to be sent 'from' their new nickname (but it was not)."); + + } finally { + tryDestroy(mucAsSeenByOne); + } + } + + /** + * Asserts that user can not change nickname to one that is already in use. + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.6", quote = + "If the user attempts to change his or her room nickname to a room nickname that is already in use by " + + "another user (or that is reserved by another user affiliated with the room, e.g., a member or owner), the " + + "service MUST deny the nickname change request and inform the user of the conflict; this is done by " + + "returning a presence stanza of type \"error\" specifying a error condition:") + public void mucBlockChangeNicknameInformationTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-blockchangenickname"); + + MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); + MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); + + final Resourcepart nicknameOne = Resourcepart.from("one-" + randomString); + final Resourcepart nicknameTwoOriginal = Resourcepart.from("two-original-" + randomString); + + createMuc(mucAsSeenByOne, nicknameOne); + + SimpleResultSyncPoint participantOneSeesTwoEnter = new SimpleResultSyncPoint(); + mucAsSeenByOne.addParticipantListener(presence -> { + if (nicknameTwoOriginal.equals(presence.getFrom().getResourceOrEmpty())) { + participantOneSeesTwoEnter.signal(); + } + }); + + // Have participant two enter the room + mucAsSeenByTwo.join(nicknameTwoOriginal); + participantOneSeesTwoEnter.waitForResult(timeout); + + try { + // Participant two changes nickname + XMPPException.XMPPErrorException conflictErrorException = assertThrows( + XMPPException.XMPPErrorException.class, () -> mucAsSeenByTwo.changeNickname(nicknameOne), + "Expected an error to be returned when '" + conTwo.getUser() + "' tried to change their nickname in room '" + mucAddress + "' to a nickname that was already in use by another occupant (but no error was returned)."); + assertNotNull(conflictErrorException, "Expected an error to be returned when '" + conTwo.getUser() + "' tried to change their nickname in room '" + mucAddress + "' to a nickname that was already in use by another occupant (but no error was returned)."); + assertNotNull(conflictErrorException.getStanzaError(), "Expected an error to be returned when '" + conTwo.getUser() + "' tried to change their nickname in room '" + mucAddress + "' to a nickname that was already in use by another occupant (but no error was returned)."); + assertEquals(StanzaError.Condition.conflict, conflictErrorException.getStanzaError().getCondition(), + "Unexpected conidtion in the (expected) error to was returned when '" + conTwo.getUser() + "' tried to change their nickname in room '" + mucAddress + "' to a nickname that was already in use by another occupant."); + } finally { + tryDestroy(mucAsSeenByOne); + } + } + + /** + * Asserts that when a user leaves a room, they are themselves included on the list of users notified (self-presence). + * + * @throws Exception when errors occur + */ + @SmackIntegrationTest(section = "7.14", quote = + "The service MUST then send a presence stanzas of type \"unavailable\" from the departing user's occupant " + + "JID to the departing occupant's full JIDs, including a status code of \"110\" to indicate that this " + + "notification is \"self-presence\"") + public void mucLeaveTest() throws Exception { + EntityBareJid mucAddress = getRandomRoom("smack-inttest-leave"); + + MultiUserChat muc = mucManagerOne.getMultiUserChat(mucAddress); + try { + muc.join(Resourcepart.from("nick-one")); + + Presence reflectedLeavePresence = muc.leave(); + + MUCUser mucUser = MUCUser.from(reflectedLeavePresence); + assertNotNull(mucUser, "Expected the reflected 'leave' presence of '" + conOne.getUser() + "' that left room '" + mucAddress + "' to include a valid 'user' child element (but it did not)."); + + assertTrue(mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110), "Expected the reflected 'leave' presence of '" + conOne.getUser() + "' that left room '" + mucAddress + "' to include status '" + MUCUser.Status.PRESENCE_TO_SELF_110 + "' (but it did not)."); + assertEquals(mucAddress + "/nick-one", reflectedLeavePresence.getFrom().toString(), "Unexpected 'from' attribute value in the reflected 'leave' presence of '" + conOne.getUser() + "' that left room '" + mucAddress + "'."); + assertEquals(conOne.getUser().asEntityFullJidIfPossible().toString(), reflectedLeavePresence.getTo().toString(), "Unexpected 'to' attribute value in the reflected 'leave' presence of '" + conOne.getUser() + "' that left room '" + mucAddress + "'."); + } finally { + muc.join(Resourcepart.from("nick-one")); // We need to be in the room to destroy the room + tryDestroy(muc); + } + } +} diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java index d26e22593..56c8a6dbb 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatRolesAffiliationsPrivilegesIntegrationTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2021 Florian Schmaus, Dan Caseley + * Copyright 2021-2024 Florian Schmaus, Dan Caseley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,61 +23,64 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.Set; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smackx.muc.MultiUserChatException.MissingMucCreationAcknowledgeException; +import org.jivesoftware.smackx.muc.MultiUserChatException.MucAlreadyJoinedException; +import org.jivesoftware.smackx.muc.MultiUserChatException.NotAMucServiceException; import org.jivesoftware.smackx.muc.packet.MUCUser; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.igniterealtime.smack.inttest.util.ResultSyncPoint; +import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityFullJid; import org.jxmpp.jid.Jid; import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.parts.Resourcepart; +import org.jxmpp.stringprep.XmppStringprepException; +@SpecificationReference(document = "XEP-0045", version = "1.34.6") public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends AbstractMultiUserChatIntegrationTest{ public MultiUserChatRolesAffiliationsPrivilegesIntegrationTest(SmackIntegrationTestEnvironment environment) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, - InterruptedException, TestNotPossibleException { + InterruptedException, TestNotPossibleException, MucAlreadyJoinedException, MissingMucCreationAcknowledgeException, NotAMucServiceException, XmppStringprepException { super(environment); } /** - * Asserts that a user who undergoes a role change receives that change as a presence update - * - *

    From XEP-0045 § 5.1.3:

    - *
    - * ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change - * to all occupants... - *
    - * - *

    From XEP-0045 § 9.6:

    - *
    - * The service MUST then send updated presence from this individual to all occupants, indicating the addition of - * moderator status... - *
    + * Asserts that a user who undergoes a role change receives that change as a presence update. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "5.1.3", quote = + "(§ 5.1.3)... a MUC service implementation MUST change the occupant's role to reflect the change and " + + "communicate the change to all occupants [...] (§ 9.6) The service MUST then send updated presence from this " + + "individual to all occupants, indicating the addition of moderator status...") public void mucRoleTestForReceivingModerator() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + final SimpleResultSyncPoint resultSyncPoint = new SimpleResultSyncPoint(); mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() { @Override public void moderatorGranted() { - resultSyncPoint.signal("done"); + resultSyncPoint.signal(); } }); @@ -90,30 +93,21 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs // success" in §9.6, since it'll throw on either an error IQ or on no response. mucAsSeenByOne.grantModerator(nicknameTwo); - resultSyncPoint.waitForResult(timeout); + assertResult(resultSyncPoint, "Expected " + conTwo.getUser() + " to get a presence update after it was granted the role 'moderator' role in " + mucAddress + " (but it did not)."); } finally { tryDestroy(mucAsSeenByOne); } } /** - * Asserts that a user who is present when another user undergoes a role change receives that change as a presence update - * - *

    From XEP-0045 § 5.1.3:

    - *
    - * ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change - * to all occupants... - *
    - * - *

    From XEP-0045 § 9.6:

    - *
    - * The service MUST then send updated presence from this individual to all occupants, indicating the addition of - * moderator status... - *
    + * Asserts that a user who is present when another user undergoes a role change receives that change as a presence update. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "9.6", quote = + "(§ 5.1.3)... a MUC service implementation MUST change the occupant's role to reflect the change and " + + "communicate the change to all occupants [...] (§ 9.6) The service MUST then send updated presence from this " + + "individual to all occupants, indicating the addition of moderator status...") public void mucRoleTestForWitnessingModerator() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); @@ -121,12 +115,12 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + final SimpleResultSyncPoint resultSyncPoint = new SimpleResultSyncPoint(); mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() { @Override public void moderatorGranted(EntityFullJid participant) { - resultSyncPoint.signal("done"); + resultSyncPoint.signal(); } }); @@ -138,7 +132,8 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs mucAsSeenByThree.join(nicknameThree); mucAsSeenByOne.grantModerator(nicknameTwo); - resultSyncPoint.waitForResult(timeout); + + assertResult(resultSyncPoint, "Expected " + conThree.getUser() + " to get a presence update after another user in the room was granted the 'moderator' role in " + mucAddress + " (but it did not)."); } finally { tryDestroy(mucAsSeenByOne); } @@ -146,35 +141,26 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs } /** - * Asserts that a user who undergoes a role change receives that change as a presence update - * - *

    From XEP-0045 § 5.1.3:

    - *
    - * ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change - * to all occupants... - *
    - * - *

    From XEP-0045 § 9.7:

    - *
    - * The service MUST then send updated presence from this individual to all occupants, indicating the removal of - * moderator status... - *
    + * Asserts that a user who undergoes a role change receives that change as a presence update. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "5.1.3", quote = + "(§ 5.1.3)... a MUC service implementation MUST change the occupant's role to reflect the change and " + + "communicate the change to all occupants [...] (§ 9.7) The service MUST then send updated presence from this " + + "individual to all occupants, indicating the removal of moderator status...") public void mucRoleTestForRemovingModerator() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + final SimpleResultSyncPoint resultSyncPoint = new SimpleResultSyncPoint(); mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() { @Override public void moderatorRevoked() { - resultSyncPoint.signal("done"); + resultSyncPoint.signal(); } }); @@ -185,30 +171,22 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs mucAsSeenByOne.grantModerator(nicknameTwo); mucAsSeenByOne.revokeModerator(nicknameTwo); - resultSyncPoint.waitForResult(timeout); + + assertResult(resultSyncPoint, "Expected " + conTwo.getUser() + " to get a presence update after its 'moderator' role in " + mucAddress + " was revoked (but it did not)."); } finally { tryDestroy(mucAsSeenByOne); } } /** - * Asserts that a user who is present when another user undergoes a role change receives that change as a presence update - * - *

    From XEP-0045 § 5.1.3:

    - *
    - * ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change - * to all occupants... - *
    - * - *

    From XEP-0045 § 9.7:

    - *
    - * The service MUST then send updated presence from this individual to all occupants, indicating the removal of - * moderator status... - *
    + * Asserts that a user who is present when another user undergoes a role change receives that change as a presence update. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "9.7", quote = + "(§ 5.1.3)... a MUC service implementation MUST change the occupant's role to reflect the change and " + + "communicate the change to all occupants [...] (§ 9.7) The service MUST then send updated presence from this " + + "individual to all occupants, indicating the removal of moderator status...") public void mucRoleTestForWitnessingModeratorRemoval() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); @@ -216,12 +194,12 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + final SimpleResultSyncPoint resultSyncPoint = new SimpleResultSyncPoint(); mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() { @Override public void moderatorRevoked(EntityFullJid participant) { - resultSyncPoint.signal("done"); + resultSyncPoint.signal(); } }); @@ -234,42 +212,33 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs mucAsSeenByOne.grantModerator(nicknameTwo); mucAsSeenByOne.revokeModerator(nicknameTwo); - resultSyncPoint.waitForResult(timeout); + assertResult(resultSyncPoint, "Expected " + conThree.getUser() + " to get a presence update after the 'moderator' role of another user in the room was revoked in " + mucAddress + " (but it did not)."); } finally { tryDestroy(mucAsSeenByOne); } } /** - * Asserts that a user in an unmoderated room who undergoes an afilliation change receives that change as a presence update - * - *

    From XEP-0045 § 5.1.3:

    - *
    - * ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change - * to all occupants... - *
    - * - *

    From XEP-0045 § 8.4:

    - *
    - * The service MUST then send updated presence from this individual to all occupants, indicating the removal of - * voice privileges... - *
    + * Asserts that a user in an unmoderated room who undergoes an affiliation change receives that change as a presence update. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "5.1.3", quote = + "(§ 5.1.3)... a MUC service implementation MUST change the occupant's role to reflect the change and " + + "communicate the change to all occupants [...] (§ 8.4) The service MUST then send updated presence from " + + "this individual to all occupants, indicating the removal of voice privileges...") public void mucRoleTestForRevokingVoice() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + final SimpleResultSyncPoint resultSyncPoint = new SimpleResultSyncPoint(); mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() { @Override public void voiceRevoked() { - resultSyncPoint.signal("done"); + resultSyncPoint.signal(); } }); @@ -278,30 +247,21 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); mucAsSeenByTwo.join(nicknameTwo); mucAsSeenByOne.revokeVoice(nicknameTwo); - resultSyncPoint.waitForResult(timeout); + assertResult(resultSyncPoint, "Expected " + conTwo.getUser() + " to get a presence update after its 'voice' privilege was revoked in " + mucAddress + " (but it did not)."); } finally { tryDestroy(mucAsSeenByOne); } } /** - * Asserts that a user who is present when another user undergoes a role change receives that change as a presence update - * - *

    From XEP-0045 § 5.1.3:

    - *
    - * ...a MUC service implementation MUST change the occupant's role to reflect the change and communicate the change - * to all occupants... - *
    - * - *

    From XEP-0045 § 8.4:

    - *
    - * The service MUST then send updated presence from this individual to all occupants, indicating the removal of - * voice privileges... - *
    + * Asserts that a user who is present when another user undergoes a role change receives that change as a presence update. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "8.4", quote = + "(§ 5.1.3)... a MUC service implementation MUST change the occupant's role to reflect the change and " + + "communicate the change to all occupants [...] (§ 8.4) The service MUST then send updated presence from " + + "this individual to all occupants, indicating the removal of voice privileges...") public void mucRoleTestForWitnessingRevokingVoice() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); @@ -309,12 +269,12 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + final SimpleResultSyncPoint resultSyncPoint = new SimpleResultSyncPoint(); mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() { @Override public void voiceRevoked(EntityFullJid participant) { - resultSyncPoint.signal("done"); + resultSyncPoint.signal(); } }); @@ -326,43 +286,34 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs mucAsSeenByThree.join(nicknameThree); mucAsSeenByOne.revokeVoice(nicknameTwo); - resultSyncPoint.waitForResult(timeout); + assertResult(resultSyncPoint, "Expected " + conThree.getUser() + " to get a presence update after another user's 'voice' privilege was revoked in " + mucAddress + " (but it did not)."); } finally { tryDestroy(mucAsSeenByOne); } } /** - * Asserts that a user who undergoes an affiliation change receives that change as a presence update - * - *

    From XEP-0045 § 5.2.2:

    - *
    - * ...a MUC service implementation MUST change the user's affiliation to reflect the change and communicate that - * to all occupants... - *
    - * - *

    From XEP-0045 § 10.6:

    - *
    - * If the user is in the room, the service MUST then send updated presence from this individual to all occupants, - * indicating the granting of admin status... - *
    + * Asserts that a user who undergoes an affiliation change receives that change as a presence update. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "5.2.2", quote = + "(§ 5.2.2) ... a MUC service implementation MUST change the user's affiliation to reflect the change and " + + "communicate that to all occupants [...] (§ 10.6) If the user is in the room, the service MUST then send " + + "updated presence from this individual to all occupants, indicating the granting of admin status...") public void mucAffiliationTestForReceivingAdmin() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + final SimpleResultSyncPoint resultSyncPoint = new SimpleResultSyncPoint(); mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() { @Override public void adminGranted() { - resultSyncPoint.signal("done"); + resultSyncPoint.signal(); } }); @@ -373,7 +324,7 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs // This implicitly tests "The service MUST add the user to the admin list and then inform the owner of success" in §10.6, since it'll throw on either an error IQ or on no response. mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); - resultSyncPoint.waitForResult(timeout); + assertResult(resultSyncPoint, "Expected " + conTwo.getUser() + " to get a presence update after its was granted 'admin' affiliation in " + mucAddress + " (but it did not)."); } finally { tryDestroy(mucAsSeenByOne); } @@ -381,23 +332,14 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs /** * Asserts that a user who is present when another user undergoes an affiliation change receives that change as a - * presence update - * - *

    From XEP-0045 § 5.2.2:

    - *
    - * ...a MUC service implementation MUST change the user's affiliation to reflect the change and communicate that - * to all occupants... - *
    - * - *

    From XEP-0045 § 10.6:

    - *
    - * If the user is in the room, the service MUST then send updated presence from this individual to all occupants, - * indicating the granting of admin status... - *
    + * presence update. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "10.6", quote = + "(§ 5.2.2) ... a MUC service implementation MUST change the user's affiliation to reflect the change and " + + "communicate that to all occupants [...] (§ 10.6) If the user is in the room, the service MUST then send " + + "updated presence from this individual to all occupants, indicating the granting of admin status...") public void mucAffiliationTestForWitnessingAdmin() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); @@ -405,12 +347,12 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + final SimpleResultSyncPoint resultSyncPoint = new SimpleResultSyncPoint(); mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() { @Override public void adminGranted(EntityFullJid participant) { - resultSyncPoint.signal("done"); + resultSyncPoint.signal(); } }); @@ -422,42 +364,34 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs mucAsSeenByThree.join(nicknameThree); mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); - resultSyncPoint.waitForResult(timeout); + assertResult(resultSyncPoint, "Expected " + conThree.getUser() + " to get a presence update after another user was granted 'admin' affiliation in " + mucAddress + " (but it did not)."); } finally { tryDestroy(mucAsSeenByOne); } } /** - * Asserts that a user who undergoes an affiliation change receives that change as a presence update - * - *

    From XEP-0045 § 5.2.2:

    - *
    - * ...a MUC service implementation MUST change the user's affiliation to reflect the change and communicate that to - * all occupants... - *
    - * - *

    From XEP-0045 § 10.7:

    - *
    - * If the user is in the room, the service MUST then send updated presence from this individual to all occupants, - * indicating the loss of admin status by sending a presence element... - *
    + * Asserts that a user who undergoes an affiliation change receives that change as a presence update. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "10.7", quote = + "(§ 5.2.2) ... a MUC service implementation MUST change the user's affiliation to reflect the change and " + + "communicate that to all occupants [...] (§ 10.6) If the user is in the room, the service MUST then send " + + "updated presence from this individual to all occupants, indicating the loss of admin status by sending a " + + "presence element...") public void mucAffiliationTestForRemovingAdmin() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress); MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + final SimpleResultSyncPoint resultSyncPoint = new SimpleResultSyncPoint(); mucAsSeenByTwo.addUserStatusListener(new UserStatusListener() { @Override public void adminRevoked() { - resultSyncPoint.signal("done"); + resultSyncPoint.signal(); } }); @@ -468,7 +402,7 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); mucAsSeenByOne.revokeAdmin(conTwo.getUser().asEntityBareJid()); - resultSyncPoint.waitForResult(timeout); + assertResult(resultSyncPoint, "Expected " + conTwo.getUser() + " to get a presence update after its 'admin' affiliation was revoked in " + mucAddress + " (but it did not)."); } finally { tryDestroy(mucAsSeenByOne); } @@ -491,7 +425,11 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "10.7", quote = + "(§ 5.2.2) ... a MUC service implementation MUST change the user's affiliation to reflect the change and " + + "communicate that to all occupants [...] (§ 10.6) If the user is in the room, the service MUST then send " + + "updated presence from this individual to all occupants, indicating the loss of admin status by sending a " + + "presence element...") public void mucAffiliationTestForWitnessingAdminRemoval() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); @@ -499,12 +437,12 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress); MultiUserChat mucAsSeenByThree = mucManagerThree.getMultiUserChat(mucAddress); - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); + final SimpleResultSyncPoint resultSyncPoint = new SimpleResultSyncPoint(); mucAsSeenByThree.addParticipantStatusListener(new ParticipantStatusListener() { @Override public void adminRevoked(EntityFullJid participant) { - resultSyncPoint.signal("done"); + resultSyncPoint.signal(); } }); @@ -517,28 +455,23 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); mucAsSeenByOne.revokeAdmin(conTwo.getUser().asEntityBareJid()); - resultSyncPoint.waitForResult(timeout); + assertResult(resultSyncPoint, "Expected " + conThree.getUser() + " to get a presence update after another user's 'admin' affiliation was revoked in " + mucAddress + " (but it did not)."); } finally { tryDestroy(mucAsSeenByOne); } } /** - * Asserts that a user who gets kicked receives that change as a presence update - * - *

    From XEP-0045 § 8.2:

    - *
    - * The kick is performed based on the occupant's room nickname and is completed by setting the role of a - * participant or visitor to a value of "none". - * - * The service MUST remove the kicked occupant by sending a presence stanza of type "unavailable" to each kicked - * occupant, including status code 307 in the extended presence information, optionally along with the reason (if - * provided) and the roomnick or bare JID of the user who initiated the kick. - *
    + * Asserts that a user who gets kicked receives that change as a presence update. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "8.2", quote = + "The kick is performed based on the occupant's room nickname and is completed by setting the role of a " + + "participant or visitor to a value of \"none\". The service MUST remove the kicked occupant by sending a " + + "presence stanza of type \"unavailable\" to each kicked occupant, including status code 307 in the extended " + + "presence information, optionally along with the reason (if provided) and the roomnick or bare JID of the " + + "user who initiated the kick.") public void mucPresenceTestForGettingKicked() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); @@ -556,35 +489,33 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs mucAsSeenByOne.kickParticipant(nicknameTwo, "Nothing personal. Just a test."); Presence kickPresence = resultSyncPoint.waitForResult(timeout); MUCUser mucUser = MUCUser.from(kickPresence); - assertNotNull(mucUser); + assertNotNull(mucUser, "Expected, but unable, to create a MUCUser instance from 'kick' presence: " + kickPresence); assertAll( - () -> assertTrue(mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110), "Missing self-presence status code in kick presence"), - () -> assertTrue(mucUser.getStatus().contains(MUCUser.Status.KICKED_307), "Missing kick status code in kick presence"), - () -> assertEquals(MUCRole.none, mucUser.getItem().getRole(), "Role other than 'none' in kick presence") + () -> assertTrue(mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110), "Missing self-presence status code in kick presence received by " + conTwo.getUser() + " after being kicked from room " + mucAddress), + () -> assertTrue(mucUser.getStatus().contains(MUCUser.Status.KICKED_307), "Missing kick status code in kick presence received by " + conTwo.getUser() + " after being kicked from room " + mucAddress), + () -> assertEquals(MUCRole.none, mucUser.getItem().getRole(), "Role other than 'none' in kick presence received by " + conTwo.getUser() + " after being kicked from room " + mucAddress) ); Jid itemJid = mucUser.getItem().getJid(); if (itemJid != null) { - assertEquals(conTwo.getUser().asEntityFullJidIfPossible(), itemJid, "Incorrect kicked user in kick presence"); + assertEquals(conTwo.getUser().asEntityFullJidIfPossible(), itemJid, "Incorrect kicked user in kick presence received by " + conTwo.getUser() + " after being kicked from room " + mucAddress); } + } catch (TimeoutException e) { + fail("Expected " + conTwo.getUser() + " to receive a presence update after it was kicked from room " + mucAddress + " (but it did not).", e); } finally { tryDestroy(mucAsSeenByOne); } } /** - * Asserts that a user who is present when another user gets kicked receives that change as a presence update - * - *

    From XEP-0045 § 8.2:

    - *
    - * ...the service MUST then inform all of the remaining occupants that the kicked occupant is no longer in the room - * by sending presence stanzas of type "unavailable" from the individual's roomnick (<room@service/nick>) to all - * the remaining occupants (just as it does when occupants exit the room of their own volition), including the - * status code and optionally the reason and actor. - *
    + * Asserts that a user who is present when another user gets kicked receives that change as a presence update. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "8.2", quote = + "...the service MUST then inform all of the remaining occupants that the kicked occupant is no longer in the " + + "room by sending presence stanzas of type \"unavailable\" from the individual's roomnick " + + "() to all the remaining occupants (just as it does when occupants exit the room of their " + + "own volition), including the status code and optionally the reason and actor.") public void mucPresenceTestForWitnessingKick() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); @@ -605,16 +536,18 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs mucAsSeenByOne.kickParticipant(nicknameTwo, "Nothing personal. Just a test."); Presence kickPresence = resultSyncPoint.waitForResult(timeout); MUCUser mucUser = MUCUser.from(kickPresence); - assertNotNull(mucUser); + assertNotNull(mucUser, "Expected, but unable, to create a MUCUser instance from 'kick' presence: " + kickPresence); assertAll( - () -> assertFalse(mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110), "Incorrect self-presence status code in kick presence"), - () -> assertTrue(mucUser.getStatus().contains(MUCUser.Status.KICKED_307), "Missing kick status code in kick presence"), - () -> assertEquals(MUCRole.none, mucUser.getItem().getRole(), "Role other than 'none' in kick presence") + () -> assertFalse(mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110), "Incorrect self-presence status code in kick presence received by " + conThree.getUser() + " after another user was kicked from room " + mucAddress), + () -> assertTrue(mucUser.getStatus().contains(MUCUser.Status.KICKED_307), "Missing kick status code in kick presence received by " + conThree.getUser() + " after another user was kicked from room " + mucAddress), + () -> assertEquals(MUCRole.none, mucUser.getItem().getRole(), "Role other than 'none' in kick presence received by " + conThree.getUser() + " after another user was kicked from room " + mucAddress) ); Jid itemJid = mucUser.getItem().getJid(); if (itemJid != null) { - assertEquals(conTwo.getUser().asEntityFullJidIfPossible(), itemJid, "Incorrect kicked user in kick presence"); + assertEquals(conTwo.getUser().asEntityFullJidIfPossible(), itemJid, "Incorrect kicked user in kick presence received by " + conThree.getUser() + " after another user was kicked from room " + mucAddress); } + } catch (TimeoutException e) { + fail("Expected " + conThree.getUser() + " to receive a presence update after another user was kicked from room " + mucAddress + " (but it did not).", e); } finally { tryDestroy(mucAsSeenByOne); } @@ -624,16 +557,12 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs /** * Asserts that an affiliation is persistent between visits to the room. * - *

    From XEP-0045 § 5.2:

    - *
    - * These affiliations are long-lived in that they persist across a user's visits to the room and are not affected - * by happenings in the room...Affiliations are granted, revoked, and maintained based on the user's bare JID, not - * the nick as with roles. - *
    - * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "5.2", quote = + "These affiliations are long-lived in that they persist across a user's visits to the room and are not " + + "affected by happenings in the room...Affiliations are granted, revoked, and maintained based on the user's " + + "bare JID, not the nick as with roles.") public void mucTestPersistentAffiliation() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); @@ -657,32 +586,24 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs mucAsSeenByThree.leave(); Presence p2 = mucAsSeenByTwo.join(nicknameTwo); Presence p3 = mucAsSeenByThree.join(nicknameThree); - assertEquals(MUCAffiliation.owner, MUCUser.from(p2).getItem().getAffiliation()); - assertEquals(MUCAffiliation.admin, MUCUser.from(p3).getItem().getAffiliation()); + assertEquals(MUCAffiliation.owner, MUCUser.from(p2).getItem().getAffiliation(), "Unexpected affiliation of " + conTwo.getUser() + " after it re-joined room " + mucAddress); + assertEquals(MUCAffiliation.admin, MUCUser.from(p3).getItem().getAffiliation(), "Unexpected affiliation of " + conThree.getUser() + " after it re-joined room " + mucAddress); } finally { tryDestroy(mucAsSeenByOne); } } /** - * Asserts that a moderator cannot revoke voice from an owner - * - *

    From XEP-0045 § 5.1.1:

    - *
    - * A moderator MUST NOT be able to revoke voice privileges from an admin or owner - *
    - * - *

    From XEP-0045 § 8.4:

    - *
    - * A moderator MUST NOT be able to revoke voice from a user whose affiliation is at or above the moderator's level. - * In addition, a service MUST NOT allow the voice privileges of an admin or owner to be removed by anyone. If a - * moderator attempts to revoke voice privileges from such a user, the service MUST deny the request and return a - * <not-allowed/> error to the sender along with the offending item(s) - *
    + * Asserts that a moderator cannot revoke voice from an owner. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "5.1.1", quote = + "A moderator MUST NOT be able to revoke voice privileges from an admin or owner [...] (§ 8.4) A moderator " + + "MUST NOT be able to revoke voice from a user whose affiliation is at or above the moderator's level. In " + + "addition, a service MUST NOT allow the voice privileges of an admin or owner to be removed by anyone. If a " + + "moderator attempts to revoke voice privileges from such a user, the service MUST deny the request and return " + + "a error to the sender along with the offending item(s)") public void mucTestModeratorCannotRevokeVoiceFromOwner() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); @@ -697,8 +618,9 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs mucAsSeenByTwo.join(nicknameTwo); mucAsSeenByOne.grantModerator(nicknameTwo); XMPPException.XMPPErrorException xe = assertThrows(XMPPException.XMPPErrorException.class, - () -> mucAsSeenByTwo.revokeVoice(nicknameOne)); - assertEquals(xe.getStanzaError().getCondition().toString(), "not-allowed"); + () -> mucAsSeenByTwo.revokeVoice(nicknameOne), + "Expected an XMPP error when " + conTwo.getUser() + " was trying to revoke the 'voice' privilege of " + conOne.getUser() + " in room " + mucAddress); + assertEquals("not-allowed", xe.getStanzaError().getCondition().toString(), "Unexpected stanza error condition in error returned when " + conTwo.getUser() + " was trying to revoke the 'voice' privilege of " + conOne.getUser() + " in room " + mucAddress); } finally { tryDestroy(mucAsSeenByOne); } @@ -708,16 +630,12 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs * Asserts that a moderator cannot revoke moderator privileges from a moderator with a higher affiliation * than themselves. * - *

    From XEP-0045 § 5.1.3 and §5.2.1:

    - *
    - * A moderator SHOULD NOT be allowed to revoke moderation privileges from someone with a higher affiliation than - * themselves (i.e., an unaffiliated moderator SHOULD NOT be allowed to revoke moderation privileges from an admin - * or an owner, and an admin SHOULD NOT be allowed to revoke moderation privileges from an owner) - *
    - * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "5.1.3", quote = + "A moderator SHOULD NOT be allowed to revoke moderation privileges from someone with a higher affiliation " + + "than themselves (i.e., an unaffiliated moderator SHOULD NOT be allowed to revoke moderation privileges from " + + "an admin or an owner, and an admin SHOULD NOT be allowed to revoke moderation privileges from an owner)") public void mucTestModeratorCannotBeRevokedFromHigherAffiliation() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest"); @@ -739,32 +657,31 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs // Admin cannot revoke from Owner XMPPException.XMPPErrorException xe1 = assertThrows(XMPPException.XMPPErrorException.class, - () -> mucAsSeenByTwo.revokeModerator(nicknameOne)); - // Moderator cannot revoke from Admin - XMPPException.XMPPErrorException xe2 = assertThrows(XMPPException.XMPPErrorException.class, - () -> mucAsSeenByThree.revokeModerator(nicknameOne)); + () -> mucAsSeenByTwo.revokeModerator(nicknameOne), + "Expected an XMPP error when " + conTwo.getUser() + " (an admin) was trying to revoke the 'moderator' role of " + conOne.getUser() + " (an owner) in room " + mucAddress); // Moderator cannot revoke from Owner + XMPPException.XMPPErrorException xe2 = assertThrows(XMPPException.XMPPErrorException.class, + () -> mucAsSeenByThree.revokeModerator(nicknameOne), + "Expected an XMPP error when " + conThree.getUser() + " (a moderator) was trying to revoke the 'moderator' role of " + conOne.getUser() + " (an owner) in room " + mucAddress); + // Moderator cannot revoke from Admin XMPPException.XMPPErrorException xe3 = assertThrows(XMPPException.XMPPErrorException.class, - () -> mucAsSeenByThree.revokeModerator(nicknameTwo)); - assertEquals(xe1.getStanzaError().getCondition().toString(), "not-allowed"); - assertEquals(xe2.getStanzaError().getCondition().toString(), "not-allowed"); - assertEquals(xe3.getStanzaError().getCondition().toString(), "not-allowed"); + () -> mucAsSeenByThree.revokeModerator(nicknameTwo), + "Expected an XMPP error when " + conThree.getUser() + " (a moderator) was trying to revoke the 'moderator' role of " + conTwo.getUser() + " (an admin) in room " + mucAddress); + assertEquals("not-allowed", xe1.getStanzaError().getCondition().toString(), "Unexpected condition in XMPP error when " + conTwo.getUser() + " (an admin) was trying to revoke the 'moderator' role of " + conOne.getUser() + " (an owner) in room " + mucAddress); + assertEquals("not-allowed", xe2.getStanzaError().getCondition().toString(), "Unexpected condition in XMPP error when " + conThree.getUser() + " (a moderator) was trying to revoke the 'moderator' role of " + conOne.getUser() + " (an owner) in room " + mucAddress); + assertEquals("not-allowed", xe3.getStanzaError().getCondition().toString(), "Unexpected condition in XMPP error when " + conThree.getUser() + " (a moderator) was trying to revoke the 'moderator' role of " + conTwo.getUser() + " (an admin) in room " + mucAddress); } finally { tryDestroy(mucAsSeenByOne); } } /** - * Asserts that an unmoderated room assigns the correct default roles for a given affiliation - * - *

    From XEP-0045 § 5.1.2:

    - *
    - * ...the initial default roles that a service SHOULD set based on the user's affiliation... - *
    + * Asserts that an unmoderated room assigns the correct default roles for a given affiliation. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "5.1.2", quote = + "...the initial default roles that a service SHOULD set based on the user's affiliation...") public void mucTestDefaultRoleForAffiliationInUnmoderatedRoom() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest-unmoderatedroles"); @@ -776,44 +693,48 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); + final EntityFullJid jidOne = JidCreate.entityFullFrom(mucAddress, nicknameOne); + final EntityFullJid jidTwo = JidCreate.entityFullFrom(mucAddress, nicknameTwo); + final EntityFullJid jidThree = JidCreate.entityFullFrom(mucAddress, nicknameThree); + createMuc(mucAsSeenByOne, nicknameOne); + + final SimpleResultSyncPoint allOccupantsDetectedSyncPoint = new SimpleResultSyncPoint(); + final Set expectedOccupants = Set.of(jidOne, jidTwo, jidThree); + mucAsSeenByOne.addParticipantStatusListener(new ParticipantStatusListener() { + @Override + public void joined(EntityFullJid participant) { + if (mucAsSeenByOne.getOccupants().containsAll(expectedOccupants)) { + allOccupantsDetectedSyncPoint.signal(); + } + } + }); + try { + mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); + mucAsSeenByTwo.join(nicknameTwo); mucAsSeenByThree.join(nicknameThree); - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); - mucAsSeenByOne.addParticipantStatusListener(new ParticipantStatusListener() { - @Override - public void adminGranted(EntityFullJid participant) { - resultSyncPoint.signal("done"); - } - }); - mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); - resultSyncPoint.waitForResult(timeout); - - assertEquals(mucAsSeenByOne.getOccupantsCount(), 3); - assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant( - JidCreate.entityFullFrom(mucAddress, nicknameOne)).getRole()); - assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant( - JidCreate.entityFullFrom(mucAddress, nicknameTwo)).getRole()); - assertEquals(MUCRole.participant, mucAsSeenByOne.getOccupant( - JidCreate.entityFullFrom(mucAddress, nicknameThree)).getRole()); + assertResult(allOccupantsDetectedSyncPoint, "Expected " + conOne.getUser() + " to observe all of these occupants in room " + mucAddress + ", but not all of them appear to be in: " + expectedOccupants.stream().map(Object::toString).collect(Collectors.joining(", "))); + assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant(JidCreate.entityFullFrom(mucAddress, nicknameOne)).getRole(), + "Unexpected role for occupant " + nicknameOne + " of " + mucAddress); + assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant(JidCreate.entityFullFrom(mucAddress, nicknameTwo)).getRole(), + "Unexpected role for occupant " + nicknameTwo + " of " + mucAddress); + assertEquals(MUCRole.participant, mucAsSeenByOne.getOccupant(JidCreate.entityFullFrom(mucAddress, nicknameThree)).getRole(), + "Unexpected role for occupant " + nicknameThree + " of " + mucAddress); } finally { tryDestroy(mucAsSeenByOne); } } /** - * Asserts that a moderated room assigns the correct default roles for a given affiliation - * - *

    From XEP-0045 § 5.1.2:

    - *
    - * ...the initial default roles that a service SHOULD set based on the user's affiliation... - *
    + * Asserts that a moderated room assigns the correct default roles for a given affiliation. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "5.1.2", quote = + "...the initial default roles that a service SHOULD set based on the user's affiliation...") public void mucTestDefaultRoleForAffiliationInModeratedRoom() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest-moderatedroles"); @@ -825,16 +746,23 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs final Resourcepart nicknameTwo = Resourcepart.from("two-" + randomString); final Resourcepart nicknameThree = Resourcepart.from("three-" + randomString); - final ResultSyncPoint resultSyncPoint = new ResultSyncPoint<>(); - mucAsSeenByOne.addParticipantStatusListener(new ParticipantStatusListener() { - @Override - public void adminGranted(EntityFullJid participant) { - resultSyncPoint.signal("done"); - } - }); + final EntityFullJid jidOne = JidCreate.entityFullFrom(mucAddress, nicknameOne); + final EntityFullJid jidTwo = JidCreate.entityFullFrom(mucAddress, nicknameTwo); + final EntityFullJid jidThree = JidCreate.entityFullFrom(mucAddress, nicknameThree); createModeratedMuc(mucAsSeenByOne, nicknameOne); + final SimpleResultSyncPoint allOccupantsDetectedSyncPoint = new SimpleResultSyncPoint(); + final Set expectedOccupants = Set.of(jidOne, jidTwo, jidThree); + mucAsSeenByOne.addParticipantStatusListener(new ParticipantStatusListener() { + @Override + public void joined(EntityFullJid participant) { + if (mucAsSeenByOne.getOccupants().containsAll(expectedOccupants)) { + allOccupantsDetectedSyncPoint.signal(); + } + } + }); + final MUCRole threeRole; switch (sinttestConfiguration.compatibilityMode) { default: @@ -846,34 +774,30 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs } try { + mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); + mucAsSeenByTwo.join(nicknameTwo); mucAsSeenByThree.join(nicknameThree); - mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); - resultSyncPoint.waitForResult(timeout); - assertEquals(mucAsSeenByOne.getOccupantsCount(), 3); - assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant( - JidCreate.entityFullFrom(mucAddress, nicknameOne)).getRole()); - assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant( - JidCreate.entityFullFrom(mucAddress, nicknameTwo)).getRole()); - assertEquals(threeRole, mucAsSeenByOne.getOccupant( - JidCreate.entityFullFrom(mucAddress, nicknameThree)).getRole()); + assertResult(allOccupantsDetectedSyncPoint, "Expected " + conOne.getUser() + " to observe all of these occupants in room " + mucAddress + ", but not all of them appear to be in: " + expectedOccupants.stream().map(Object::toString).collect(Collectors.joining(", "))); + assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant(JidCreate.entityFullFrom(mucAddress, nicknameOne)).getRole(), + "Unexpected role for occupant " + nicknameOne + " of " + mucAddress); + assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant(JidCreate.entityFullFrom(mucAddress, nicknameTwo)).getRole(), + "Unexpected role for occupant " + nicknameTwo + " of " + mucAddress); + assertEquals(threeRole, mucAsSeenByOne.getOccupant(JidCreate.entityFullFrom(mucAddress, nicknameThree)).getRole(), + "Unexpected role for occupant " + nicknameThree + " of " + mucAddress); } finally { tryDestroy(mucAsSeenByOne); } } /** - * Asserts that a members-only room assigns the correct default roles for a given affiliation - * - *

    From XEP-0045 § 5.1.2:

    - *
    - * ...the initial default roles that a service SHOULD set based on the user's affiliation... - *
    + * Asserts that a members-only room assigns the correct default roles for a given affiliation. * * @throws Exception when errors occur */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "5.1.2", quote = + "...the initial default roles that a service SHOULD set based on the user's affiliation...") public void mucTestDefaultRoleForAffiliationInMembersOnlyRoom() throws Exception { EntityBareJid mucAddress = getRandomRoom("smack-inttest-membersonlyroles"); @@ -891,26 +815,29 @@ public class MultiUserChatRolesAffiliationsPrivilegesIntegrationTest extends Abs createMembersOnlyMuc(mucAsSeenByOne, nicknameOne); - final ResultSyncPoint adminResultSyncPoint = new ResultSyncPoint<>(); + final SimpleResultSyncPoint allOccupantsDetectedSyncPoint = new SimpleResultSyncPoint(); + final Set expectedOccupants = Set.of(jidOne, jidTwo, jidThree); mucAsSeenByOne.addParticipantStatusListener(new ParticipantStatusListener() { @Override - public void adminGranted(EntityFullJid participant) { - adminResultSyncPoint.signal("done"); + public void joined(EntityFullJid participant) { + if (mucAsSeenByOne.getOccupants().containsAll(expectedOccupants)) { + allOccupantsDetectedSyncPoint.signal(); + } } }); try { mucAsSeenByOne.grantMembership(conTwo.getUser().asBareJid()); mucAsSeenByOne.grantMembership(conThree.getUser().asBareJid()); + mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); mucAsSeenByTwo.join(nicknameTwo); mucAsSeenByThree.join(nicknameThree); - mucAsSeenByOne.grantAdmin(conTwo.getUser().asBareJid()); - adminResultSyncPoint.waitForResult(timeout); - assertEquals(mucAsSeenByOne.getOccupantsCount(), 3); - assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant(jidOne).getRole()); - assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant(jidTwo).getRole()); - assertEquals(MUCRole.participant, mucAsSeenByOne.getOccupant(jidThree).getRole()); + + assertResult(allOccupantsDetectedSyncPoint, "Expected " + conOne.getUser() + " to observe all of these occupants in room " + mucAddress + ", but not all of them appear to be in: " + expectedOccupants.stream().map(Object::toString).collect(Collectors.joining(", "))); + assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant(jidOne).getRole(), "Unexpected role for occupant " + jidOne + " in room " + mucAddress); + assertEquals(MUCRole.moderator, mucAsSeenByOne.getOccupant(jidTwo).getRole(), "Unexpected role for occupant " + jidTwo + " in room " + mucAddress); + assertEquals(MUCRole.participant, mucAsSeenByOne.getOccupant(jidThree).getRole(), "Unexpected role for occupant " + jidThree + " in room " + mucAddress); } finally { tryDestroy(mucAsSeenByOne); } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/ParticipantStatusIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/ParticipantStatusIntegrationTest.java new file mode 100644 index 000000000..1bf3fdce4 --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/ParticipantStatusIntegrationTest.java @@ -0,0 +1,133 @@ +/** + * + * Copyright 2024 Guus der Kinderen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.muc; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; + +import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; +import org.igniterealtime.smack.inttest.TestNotPossibleException; +import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; +import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.EntityFullJid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.jid.parts.Resourcepart; +import org.jxmpp.stringprep.XmppStringprepException; + +/** + * Tests that verify the correct functionality of Smack's {@link ParticipantStatusListener}. + */ +@SpecificationReference(document = "XEP-0045", version = "1.34.6") +public class ParticipantStatusIntegrationTest extends AbstractMultiUserChatIntegrationTest { + + public ParticipantStatusIntegrationTest(SmackIntegrationTestEnvironment environment) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, TestNotPossibleException, MultiUserChatException.MucAlreadyJoinedException, MultiUserChatException.MissingMucCreationAcknowledgeException, XmppStringprepException, MultiUserChatException.NotAMucServiceException { + super(environment); + } + + /** + * Verifies that when a member gets its membership removed in an open room, the appropriate event listener is invoked. + * + * @throws Exception On unexpected results + */ + @SmackIntegrationTest(section = "9.4", quote = "An admin might want to revoke a user's membership [...] The service MUST then send updated presence from this individual to all occupants, indicating the loss of membership by sending a presence element that contains an element qualified by the 'http://jabber.org/protocol/muc#user' namespace and containing an child with the 'affiliation' attribute set to a value of \"none\".") + public void testMembershipRevokedInOpenRoom() throws Exception { + // Setup test fixture. + final EntityBareJid mucAddress = getRandomRoom("smack-inttest-participantstatus-membership-revoked-open"); + final MultiUserChat mucAsSeenByOwner = mucManagerOne.getMultiUserChat(mucAddress); + final MultiUserChat mucAsSeenByTarget = mucManagerTwo.getMultiUserChat(mucAddress); + + final EntityFullJid mucAddressOwner = JidCreate.entityFullFrom(mucAddress, Resourcepart.from("owner-" + randomString)); + final EntityFullJid mucAddressTarget = JidCreate.entityFullFrom(mucAddress, Resourcepart.from("target-" + randomString)); + + createMuc(mucAsSeenByOwner, mucAddressOwner.getResourcepart()); + try { + mucAsSeenByOwner.grantMembership(conTwo.getUser().asBareJid()); + mucAsSeenByTarget.join(mucAddressTarget.getResourcepart()); + + final SimpleResultSyncPoint ownerSeesRevoke = new SimpleResultSyncPoint(); + mucAsSeenByOwner.addParticipantStatusListener(new ParticipantStatusListener() { + @Override + public void membershipRevoked(EntityFullJid participant) { + if (mucAddressTarget.equals(participant)) { + ownerSeesRevoke.signal(); + } + } + }); + + // Execute system under test. + mucAsSeenByOwner.revokeMembership(conTwo.getUser().asBareJid()); + + // Verify result. + assertResult(ownerSeesRevoke, "Expected '" + conOne.getUser() + "' to be notified of the revocation of membership of '" + conTwo.getUser() + "' (using nickname '" + mucAddressTarget.getResourcepart() + "') in '" + mucAddress + "' (but did not)."); + } finally { + // Clean up test fixture. + tryDestroy(mucAsSeenByOwner); + } + } + + /** + * Verifies that when a member gets its membership removed in a members-only room, the appropriate event listeners are invoked. + * + * @throws Exception On unexpected results + */ + @SmackIntegrationTest(section = "9.4", quote = "An admin might want to revoke a user's membership [...] If the room is members-only, the service MUST remove the user from the room, including a status code of 321 to indicate that the user was removed because of an affiliation change, and inform all remaining occupants") + public void testMembershipRevokedInMemberOnlyRoom() throws Exception { + // Setup test fixture. + final EntityBareJid mucAddress = getRandomRoom("smack-inttest-participantstatus-membership-revoked-membersonly"); + final MultiUserChat mucAsSeenByOwner = mucManagerOne.getMultiUserChat(mucAddress); + final MultiUserChat mucAsSeenByTarget = mucManagerTwo.getMultiUserChat(mucAddress); + + final EntityFullJid mucAddressOwner = JidCreate.entityFullFrom(mucAddress, Resourcepart.from("owner-" + randomString)); + final EntityFullJid mucAddressTarget = JidCreate.entityFullFrom(mucAddress, Resourcepart.from("target-" + randomString)); + + createMembersOnlyMuc(mucAsSeenByOwner, mucAddressOwner.getResourcepart()); + try { + mucAsSeenByOwner.grantMembership(conTwo.getUser().asBareJid()); + mucAsSeenByTarget.join(mucAddressTarget.getResourcepart()); + + final SimpleResultSyncPoint ownerSeesRevoke = new SimpleResultSyncPoint(); + final SimpleResultSyncPoint ownerSeesDeparture = new SimpleResultSyncPoint(); + mucAsSeenByOwner.addParticipantStatusListener(new ParticipantStatusListener() { + @Override + public void membershipRevoked(EntityFullJid participant) { + if (mucAddressTarget.equals(participant)) { + ownerSeesRevoke.signal(); + } + } + + @Override + public void parted(EntityFullJid participant) { + if (mucAddressTarget.equals(participant)) { + ownerSeesDeparture.signal(); + } + } + }); + + // Execute system under test. + mucAsSeenByOwner.revokeMembership(conTwo.getUser().asBareJid()); + + // Verify result. + assertResult(ownerSeesRevoke, "Expected '" + conOne.getUser() + "' to be notified of the revocation of membership of '" + conTwo.getUser() + "' (using nickname '" + mucAddressTarget.getResourcepart() + "') in '" + mucAddress + "' (but did not)."); + assertResult(ownerSeesDeparture, "Expected '" + conOne.getUser() + "' to be notified of '" + conTwo.getUser() + "' (using nickname '" + mucAddressTarget.getResourcepart() + "') departing '" + mucAddress + "' (but did not)."); + } finally { + // Clean up test fixture. + tryDestroy(mucAsSeenByOwner); + } + } +} diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/UserStatusIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/UserStatusIntegrationTest.java new file mode 100644 index 000000000..201f1ba21 --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/UserStatusIntegrationTest.java @@ -0,0 +1,129 @@ +/** + * + * Copyright 2024 Guus der Kinderen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.muc; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smackx.muc.packet.MUCUser; + +import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; +import org.igniterealtime.smack.inttest.TestNotPossibleException; +import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; +import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.EntityFullJid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.jid.parts.Resourcepart; +import org.jxmpp.stringprep.XmppStringprepException; + +/** + * Tests that verify the correct functionality of Smack's {@link UserStatusListener}. + */ +@SpecificationReference(document = "XEP-0045", version = "1.34.6") +public class UserStatusIntegrationTest extends AbstractMultiUserChatIntegrationTest { + + public UserStatusIntegrationTest(SmackIntegrationTestEnvironment environment) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, TestNotPossibleException, MultiUserChatException.MucAlreadyJoinedException, MultiUserChatException.MissingMucCreationAcknowledgeException, XmppStringprepException, MultiUserChatException.NotAMucServiceException { + super(environment); + } + + /** + * Verifies that when a member gets its membership removed in an open room, the appropriate event listener is invoked. + * + * @throws Exception On unexpected results + */ + @SmackIntegrationTest(section = "9.4", quote = "An admin might want to revoke a user's membership [...] The service MUST then send updated presence from this individual to all occupants, indicating the loss of membership by sending a presence element that contains an element qualified by the 'http://jabber.org/protocol/muc#user' namespace and containing an child with the 'affiliation' attribute set to a value of \"none\".") + public void testMembershipRevokedInOpenRoom() throws Exception { + // Setup test fixture. + final EntityBareJid mucAddress = getRandomRoom("smack-inttest-userstatus-membership-revoked-membersonly"); + final MultiUserChat mucAsSeenByOwner = mucManagerOne.getMultiUserChat(mucAddress); + final MultiUserChat mucAsSeenByTarget = mucManagerTwo.getMultiUserChat(mucAddress); + + final EntityFullJid mucAddressOwner = JidCreate.entityFullFrom(mucAddress, Resourcepart.from("owner-" + randomString)); + final EntityFullJid mucAddressTarget = JidCreate.entityFullFrom(mucAddress, Resourcepart.from("target-" + randomString)); + + createMuc(mucAsSeenByOwner, mucAddressOwner.getResourcepart()); + try { + mucAsSeenByOwner.grantMembership(conTwo.getUser().asBareJid()); + mucAsSeenByTarget.join(mucAddressTarget.getResourcepart()); + + final SimpleResultSyncPoint targetSeesRevoke = new SimpleResultSyncPoint(); + mucAsSeenByTarget.addUserStatusListener(new UserStatusListener() { + @Override + public void membershipRevoked() { + targetSeesRevoke.signal(); + } + }); + + // Execute system under test. + mucAsSeenByOwner.revokeMembership(conTwo.getUser().asBareJid()); + + // Verify result. + assertResult(targetSeesRevoke, "Expected '" + conTwo.getUser() + "' (using nickname '" + mucAddressTarget.getResourcepart() + "') to be notified that their membership status was removed by '" + conOne.getUser() + "' (using nickname '" + mucAddressOwner.getResourcepart() + "') in '" + mucAddress + "' (but did not)."); + } finally { + // Clean up test fixture. + tryDestroy(mucAsSeenByOwner); + } + } + + /** + * Verifies that when a member gets its membership removed in a members-only room, the appropriate event listeners are invoked. + * + * @throws Exception On unexpected results + */ + @SmackIntegrationTest(section = "9.4", quote = "An admin might want to revoke a user's membership [...] If the room is members-only, the service MUST remove the user from the room, including a status code of 321 to indicate that the user was removed because of an affiliation change, and inform all remaining occupants") + public void testMembershipRevokedInMemberOnlyRoom() throws Exception { + // Setup test fixture. + final EntityBareJid mucAddress = getRandomRoom("smack-inttest-userstatus-membership-revoked-membersonly"); + final MultiUserChat mucAsSeenByOwner = mucManagerOne.getMultiUserChat(mucAddress); + final MultiUserChat mucAsSeenByTarget = mucManagerTwo.getMultiUserChat(mucAddress); + + final EntityFullJid mucAddressOwner = JidCreate.entityFullFrom(mucAddress, Resourcepart.from("owner-" + randomString)); + final EntityFullJid mucAddressTarget = JidCreate.entityFullFrom(mucAddress, Resourcepart.from("target-" + randomString)); + + createMembersOnlyMuc(mucAsSeenByOwner, mucAddressOwner.getResourcepart()); + try { + mucAsSeenByOwner.grantMembership(conTwo.getUser().asBareJid()); + mucAsSeenByTarget.join(mucAddressTarget.getResourcepart()); + + final SimpleResultSyncPoint targetSeesRevoke = new SimpleResultSyncPoint(); + final SimpleResultSyncPoint targetSeesRemove = new SimpleResultSyncPoint(); + mucAsSeenByTarget.addUserStatusListener(new UserStatusListener() { + @Override + public void removed(MUCUser mucUser, Presence presence) { + targetSeesRemove.signal(); + } + + @Override + public void membershipRevoked() { + targetSeesRevoke.signal(); + } + }); + + // Execute system under test. + mucAsSeenByOwner.revokeMembership(conTwo.getUser().asBareJid()); + + // Verify result. + assertResult(targetSeesRemove, "Expected '" + conTwo.getUser() + "' (using nickname '" + mucAddressTarget.getResourcepart() + "') to be notified that it is removed from '" + mucAddress + "' which is a member-only room, as their membership status was removed by '" + conOne.getUser() + "' (using nickname '" + mucAddressOwner.getResourcepart() + "') (but did not)."); + assertResult(targetSeesRevoke, "Expected '" + conTwo.getUser() + "' (using nickname '" + mucAddressTarget.getResourcepart() + "') to be notified that their membership status was removed by '" + conOne.getUser() + "' (using nickname '" + mucAddressOwner.getResourcepart() + "') in '" + mucAddress + "' (but did not)."); + } finally { + // Clean up test fixture. + tryDestroy(mucAsSeenByOwner); + } + } +} diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractTwoUsersOmemoIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractTwoUsersOmemoIntegrationTest.java index 1720c0725..995fc14a8 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractTwoUsersOmemoIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractTwoUsersOmemoIntegrationTest.java @@ -17,7 +17,7 @@ package org.jivesoftware.smackx.omemo; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import java.io.IOException; @@ -51,7 +51,9 @@ public abstract class AbstractTwoUsersOmemoIntegrationTest extends AbstractOmemo alice = OmemoManagerSetupHelper.prepareOmemoManager(conOne); bob = OmemoManagerSetupHelper.prepareOmemoManager(conTwo); - assertFalse(alice.getDeviceId().equals(bob.getDeviceId())); + // TODO is this a test assertion, or a bug in the test implementation (in which case an Exception should be thrown instead). + assertNotEquals(alice.getDeviceId(), bob.getDeviceId(), + "Expected device ID for " + conOne.getUser() + " to differ from that of " + conTwo.getUser() + " (but they did not)"); // Subscribe presences IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(alice.getConnection(), bob.getConnection(), timeout); @@ -59,8 +61,10 @@ public abstract class AbstractTwoUsersOmemoIntegrationTest extends AbstractOmemo OmemoManagerSetupHelper.trustAllIdentitiesWithTests(alice, bob); // Alice trusts Bob's devices OmemoManagerSetupHelper.trustAllIdentitiesWithTests(bob, alice); // Bob trusts Alice' and Mallory's devices - assertEquals(bob.getOwnFingerprint(), alice.getActiveFingerprints(bob.getOwnJid()).get(bob.getOwnDevice())); - assertEquals(alice.getOwnFingerprint(), bob.getActiveFingerprints(alice.getOwnJid()).get(alice.getOwnDevice())); + assertEquals(bob.getOwnFingerprint(), alice.getActiveFingerprints(bob.getOwnJid()).get(bob.getOwnDevice()), + "Expected fingerprint of " + conTwo.getUser() + "'s device as known to " + conOne.getUser() + " to be equal to " + conTwo.getUser() + "'s own fingerprint (but it was not)."); + assertEquals(alice.getOwnFingerprint(), bob.getActiveFingerprints(alice.getOwnJid()).get(alice.getOwnDevice()), + "Expected fingerprint of " + conOne.getUser() + "'s device as known to " + conTwo.getUser() + " to be equal to " + conOne.getUser() + "'s own fingerprint (but it was not)."); } @AfterClass diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/MessageEncryptionIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/MessageEncryptionIntegrationTest.java index 2efd1fb1a..5b489aef7 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/MessageEncryptionIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/MessageEncryptionIntegrationTest.java @@ -29,12 +29,14 @@ import org.jivesoftware.smackx.omemo.element.OmemoBundleElement; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; /** * Simple OMEMO message encryption integration test. * During this test Alice sends an encrypted message to Bob. Bob decrypts it and sends a response to Alice. * It is checked whether the messages can be decrypted, and if used up pre-keys result in renewed bundles. */ +@SpecificationReference(document = "XEP-0384", version = "0.3.0") public class MessageEncryptionIntegrationTest extends AbstractTwoUsersOmemoIntegrationTest { public MessageEncryptionIntegrationTest(SmackIntegrationTestEnvironment environment) diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoMamDecryptionTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoMamDecryptionTest.java index 442643071..f14fd1990 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoMamDecryptionTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoMamDecryptionTest.java @@ -35,11 +35,13 @@ import org.jivesoftware.smackx.omemo.util.MessageOrOmemoMessage; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; /** * This test sends a message from Alice to Bob, while Bob has automatic decryption disabled. * Then Bob fetches his Mam archive and decrypts the result. */ +@SpecificationReference(document = "XEP-0384", version = "0.3.0") public class OmemoMamDecryptionTest extends AbstractTwoUsersOmemoIntegrationTest { public OmemoMamDecryptionTest(SmackIntegrationTestEnvironment environment) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, @@ -71,11 +73,12 @@ public class OmemoMamDecryptionTest extends AbstractTwoUsersOmemoIntegrationTest alicesConnection.sendStanza(encrypted.buildMessage(messageBuilder, bob.getOwnJid())); MamManager.MamQuery query = bobsMamManager.queryArchive(MamManager.MamQueryArgs.builder().limitResultsToJid(alice.getOwnJid()).build()); - assertEquals(1, query.getMessageCount()); + assertEquals(1, query.getMessageCount(), "Unexpected message count in MAM query result of " + bob.getConnection().getUser()); List decryptedMamQuery = bob.decryptMamQueryResult(query); - assertEquals(1, decryptedMamQuery.size()); - assertEquals(body, decryptedMamQuery.get(decryptedMamQuery.size() - 1).getOmemoMessage().getBody()); + assertEquals(1, decryptedMamQuery.size(), "Unexpected decrypted message count in MAM query result of " + bob.getConnection().getUser()); + assertEquals(body, decryptedMamQuery.get(decryptedMamQuery.size() - 1).getOmemoMessage().getBody(), + "Expected decrypted body of message retrieved via a MAM query to be equal to the original body that was sent (but it was not)."); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoManagerSetupHelper.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoManagerSetupHelper.java index 1a8093d63..7fdf53165 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoManagerSetupHelper.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoManagerSetupHelper.java @@ -21,8 +21,8 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; -import java.util.HashMap; import java.util.List; +import java.util.Map; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.NotConnectedException; @@ -54,7 +54,7 @@ public class OmemoManagerSetupHelper { } alice.requestDeviceListUpdateFor(bob.getOwnJid()); - HashMap fingerprints = alice.getActiveFingerprints(bob.getOwnJid()); + Map fingerprints = alice.getActiveFingerprints(bob.getOwnJid()); for (OmemoDevice device : fingerprints.keySet()) { OmemoFingerprint fingerprint = fingerprints.get(device); @@ -67,7 +67,7 @@ public class OmemoManagerSetupHelper { SmackException.NoResponseException, CannotEstablishOmemoSessionException, CorruptedOmemoKeyException, XMPPException.XMPPErrorException, PubSubException.NotALeafNodeException, IOException { alice.requestDeviceListUpdateFor(bob.getOwnJid()); - HashMap fps1 = alice.getActiveFingerprints(bob.getOwnJid()); + Map fps1 = alice.getActiveFingerprints(bob.getOwnJid()); assertFalse(fps1.isEmpty()); assertAllDevicesAreUndecided(alice, fps1); @@ -75,7 +75,7 @@ public class OmemoManagerSetupHelper { trustAllIdentities(alice, bob); - HashMap fps2 = alice.getActiveFingerprints(bob.getOwnJid()); + Map fps2 = alice.getActiveFingerprints(bob.getOwnJid()); assertEquals(fps1.size(), fps2.size()); assertTrue(Maps.difference(fps1, fps2).areEqual()); @@ -95,28 +95,28 @@ public class OmemoManagerSetupHelper { return manager; } - public static void assertAllDevicesAreUndecided(OmemoManager manager, HashMap devices) { + public static void assertAllDevicesAreUndecided(OmemoManager manager, Map devices) { for (OmemoDevice device : devices.keySet()) { // All fingerprints MUST be neither decided, nor trusted. assertFalse(manager.isDecidedOmemoIdentity(device, devices.get(device))); } } - public static void assertAllDevicesAreUntrusted(OmemoManager manager, HashMap devices) { + public static void assertAllDevicesAreUntrusted(OmemoManager manager, Map devices) { for (OmemoDevice device : devices.keySet()) { // All fingerprints MUST be neither decided, nor trusted. assertFalse(manager.isTrustedOmemoIdentity(device, devices.get(device))); } } - public static void assertAllDevicesAreDecided(OmemoManager manager, HashMap devices) { + public static void assertAllDevicesAreDecided(OmemoManager manager, Map devices) { for (OmemoDevice device : devices.keySet()) { // All fingerprints MUST be neither decided, nor trusted. assertTrue(manager.isDecidedOmemoIdentity(device, devices.get(device))); } } - public static void assertAllDevicesAreTrusted(OmemoManager manager, HashMap devices) { + public static void assertAllDevicesAreTrusted(OmemoManager manager, Map devices) { for (OmemoDevice device : devices.keySet()) { // All fingerprints MUST be neither decided, nor trusted. assertTrue(manager.isTrustedOmemoIdentity(device, devices.get(device))); diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java index 94c671962..b4daa8640 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java @@ -32,7 +32,9 @@ import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; +@SpecificationReference(document = "XEP-0384", version = "0.3.0") public class ReadOnlyDeviceIntegrationTest extends AbstractTwoUsersOmemoIntegrationTest { public ReadOnlyDeviceIntegrationTest(SmackIntegrationTestEnvironment environment) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, TestNotPossibleException { diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/SessionRenegotiationIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/SessionRenegotiationIntegrationTest.java index c4a649911..0b3b15e79 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/SessionRenegotiationIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/SessionRenegotiationIntegrationTest.java @@ -24,7 +24,9 @@ import org.jivesoftware.smack.packet.MessageBuilder; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; +@SpecificationReference(document = "XEP-0384", version = "0.3.0") public class SessionRenegotiationIntegrationTest extends AbstractTwoUsersOmemoIntegrationTest { public SessionRenegotiationIntegrationTest(SmackIntegrationTestEnvironment environment) diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/OXSecretKeyBackupIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/OXSecretKeyBackupIntegrationTest.java index 60e3361ad..93f936785 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/OXSecretKeyBackupIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/OXSecretKeyBackupIntegrationTest.java @@ -38,6 +38,7 @@ import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException; import org.jivesoftware.smackx.ox.exception.NoBackupFoundException; import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore; import org.jivesoftware.smackx.ox.store.filebased.FileBasedOpenPgpStore; +import org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil; import org.jivesoftware.smackx.pubsub.PubSubException; import org.bouncycastle.openpgp.PGPException; @@ -48,9 +49,11 @@ import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.AfterClass; import org.igniterealtime.smack.inttest.annotations.BeforeClass; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.key.protection.UnprotectedKeysProtector; +@SpecificationReference(document = "XEP-0374", version = "0.2.0") public class OXSecretKeyBackupIntegrationTest extends AbstractOpenPgpIntegrationTest { private static final String sessionId = StringUtils.randomString(10); @@ -101,7 +104,7 @@ public class OXSecretKeyBackupIntegrationTest extends AbstractOpenPgpIntegration org.apache.commons.io.FileUtils.deleteDirectory(beforePath); } - @SmackIntegrationTest + @SmackIntegrationTest(section = "5") public void test() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException, IOException, InterruptedException, PubSubException.NotALeafNodeException, SmackException.NoResponseException, SmackException.NotConnectedException, XMPPException.XMPPErrorException, @@ -120,38 +123,43 @@ public class OXSecretKeyBackupIntegrationTest extends AbstractOpenPgpIntegration assertNull(self.getSigningKeyFingerprint()); OpenPgpV4Fingerprint keyFingerprint = openPgpManager.generateAndImportKeyPair(alice); - assertEquals(keyFingerprint, self.getSigningKeyFingerprint()); - assertTrue(self.getSecretKeys().contains(keyFingerprint.getKeyId())); + try { + assertEquals(keyFingerprint, self.getSigningKeyFingerprint()); + assertTrue(self.getSecretKeys().contains(keyFingerprint.getKeyId())); - PGPSecretKeyRing beforeSec = beforeStore.getSecretKeyRing(alice, keyFingerprint); - assertNotNull(beforeSec); + PGPSecretKeyRing beforeSec = beforeStore.getSecretKeyRing(alice, keyFingerprint); + assertNotNull(beforeSec); - PGPPublicKeyRing beforePub = beforeStore.getPublicKeyRing(alice, keyFingerprint); - assertNotNull(beforePub); + PGPPublicKeyRing beforePub = beforeStore.getPublicKeyRing(alice, keyFingerprint); + assertNotNull(beforePub); - OpenPgpSecretKeyBackupPassphrase backupPassphrase = + OpenPgpSecretKeyBackupPassphrase backupPassphrase = openPgpManager.backupSecretKeyToServer(availableSecretKeys -> availableSecretKeys); - FileBasedOpenPgpStore afterStore = new FileBasedOpenPgpStore(afterPath); - afterStore.setKeyRingProtector(new UnprotectedKeysProtector()); - PainlessOpenPgpProvider afterProvider = new PainlessOpenPgpProvider(afterStore); - openPgpManager.setOpenPgpProvider(afterProvider); + FileBasedOpenPgpStore afterStore = new FileBasedOpenPgpStore(afterPath); + afterStore.setKeyRingProtector(new UnprotectedKeysProtector()); + PainlessOpenPgpProvider afterProvider = new PainlessOpenPgpProvider(afterStore); + openPgpManager.setOpenPgpProvider(afterProvider); - OpenPgpV4Fingerprint fingerprint = openPgpManager.restoreSecretKeyServerBackup(() -> backupPassphrase); + OpenPgpV4Fingerprint fingerprint = openPgpManager.restoreSecretKeyServerBackup(() -> backupPassphrase); - assertEquals(keyFingerprint, fingerprint); + assertEquals(keyFingerprint, fingerprint); - assertTrue(self.getSecretKeys().contains(keyFingerprint.getKeyId())); + assertTrue(self.getSecretKeys().contains(keyFingerprint.getKeyId())); - assertEquals(keyFingerprint, self.getSigningKeyFingerprint()); + assertEquals(keyFingerprint, self.getSigningKeyFingerprint()); - PGPSecretKeyRing afterSec = afterStore.getSecretKeyRing(alice, keyFingerprint); - assertNotNull(afterSec); - assertArrayEquals(beforeSec.getEncoded(), afterSec.getEncoded()); + PGPSecretKeyRing afterSec = afterStore.getSecretKeyRing(alice, keyFingerprint); + assertNotNull(afterSec); + assertArrayEquals(beforeSec.getEncoded(), afterSec.getEncoded()); - PGPPublicKeyRing afterPub = afterStore.getPublicKeyRing(alice, keyFingerprint); - assertNotNull(afterPub); - assertArrayEquals(beforePub.getEncoded(), afterPub.getEncoded()); + PGPPublicKeyRing afterPub = afterStore.getPublicKeyRing(alice, keyFingerprint); + assertNotNull(afterPub); + assertArrayEquals(beforePub.getEncoded(), afterPub.getEncoded()); + } finally { + OpenPgpPubSubUtil.deletePublicKeyNode(alicePepManager, keyFingerprint); + OpenPgpPubSubUtil.deletePubkeysListNode(alicePepManager); + } } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingIntegrationTest.java index 683bc199a..4467dde1f 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingIntegrationTest.java @@ -33,17 +33,20 @@ import org.jivesoftware.smackx.ox.OpenPgpManager; import org.jivesoftware.smackx.ox.crypto.PainlessOpenPgpProvider; import org.jivesoftware.smackx.ox.element.SigncryptElement; import org.jivesoftware.smackx.ox.store.filebased.FileBasedOpenPgpStore; +import org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.AfterClass; import org.igniterealtime.smack.inttest.annotations.BeforeClass; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; import org.pgpainless.decryption_verification.OpenPgpMetadata; import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.key.protection.UnprotectedKeysProtector; +@SpecificationReference(document = "XEP-0374", version = "0.2.0") public class OXInstantMessagingIntegrationTest extends AbstractOpenPgpIntegrationTest { private static final String sessionId = StringUtils.randomString(10); @@ -137,26 +140,33 @@ public class OXInstantMessagingIntegrationTest extends AbstractOpenPgpIntegratio aliceFingerprint = aliceOpenPgp.generateAndImportKeyPair(alice); bobFingerprint = bobOpenPgp.generateAndImportKeyPair(bob); - aliceOpenPgp.announceSupportAndPublish(); - bobOpenPgp.announceSupportAndPublish(); + try { + aliceOpenPgp.announceSupportAndPublish(); + bobOpenPgp.announceSupportAndPublish(); - OpenPgpContact bobForAlice = aliceOpenPgp.getOpenPgpContact(bob.asEntityBareJidIfPossible()); - OpenPgpContact aliceForBob = bobOpenPgp.getOpenPgpContact(alice.asEntityBareJidIfPossible()); + OpenPgpContact bobForAlice = aliceOpenPgp.getOpenPgpContact(bob.asEntityBareJidIfPossible()); + OpenPgpContact aliceForBob = bobOpenPgp.getOpenPgpContact(alice.asEntityBareJidIfPossible()); - bobForAlice.updateKeys(aliceConnection); + bobForAlice.updateKeys(aliceConnection); - assertFalse(bobForAlice.isTrusted(bobFingerprint)); - assertFalse(aliceForBob.isTrusted(aliceFingerprint)); + assertFalse(bobForAlice.isTrusted(bobFingerprint)); + assertFalse(aliceForBob.isTrusted(aliceFingerprint)); - bobForAlice.trust(bobFingerprint); - aliceForBob.trust(aliceFingerprint); + bobForAlice.trust(bobFingerprint); + aliceForBob.trust(aliceFingerprint); - assertTrue(bobForAlice.isTrusted(bobFingerprint)); - assertTrue(aliceForBob.isTrusted(aliceFingerprint)); + assertTrue(bobForAlice.isTrusted(bobFingerprint)); + assertTrue(aliceForBob.isTrusted(aliceFingerprint)); - aliceInstantMessaging.sendOxMessage(bobForAlice, body); + aliceInstantMessaging.sendOxMessage(bobForAlice, body); - bobReceivedMessage.waitForResult(timeout); + bobReceivedMessage.waitForResult(timeout); + } finally { + OpenPgpPubSubUtil.deletePublicKeyNode(alicePepManager, aliceFingerprint); + OpenPgpPubSubUtil.deletePubkeysListNode(alicePepManager); + OpenPgpPubSubUtil.deletePublicKeyNode(bobPepManager, bobFingerprint); + OpenPgpPubSubUtil.deletePubkeysListNode(bobPepManager); + } } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/package-info.java index 7ebe86244..bce3b6064 120000 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/package-info.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/package-info.java @@ -1 +1 @@ -../../../../../../../smack-java8-full/src/main/java/org/jivesoftware/smackx/package-info.java \ No newline at end of file +../../../../../../../smack-java11-full/src/main/java/org/jivesoftware/smackx/package-info.java \ No newline at end of file diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ping/PingIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ping/PingIntegrationTest.java index 65c683093..a2cbce7ce 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ping/PingIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ping/PingIntegrationTest.java @@ -35,8 +35,10 @@ import org.jivesoftware.smack.XMPPConnection; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.jxmpp.jid.Jid; +@SpecificationReference(document = "XEP-0199", version = "2.0.1") public class PingIntegrationTest extends AbstractSmackIntegrationTest { public PingIntegrationTest(SmackIntegrationTestEnvironment environment) { diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/pubsub/PubSubIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/pubsub/PubSubIntegrationTest.java index 1890caf2d..d3f990a4a 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/pubsub/PubSubIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/pubsub/PubSubIntegrationTest.java @@ -34,8 +34,10 @@ import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.jxmpp.jid.DomainBareJid; +@SpecificationReference(document = "XEP-0060", version = "1.26.0") public class PubSubIntegrationTest extends AbstractSmackIntegrationTest { private final PubSubManager pubSubManagerOne; @@ -82,21 +84,10 @@ public class PubSubIntegrationTest extends AbstractSmackIntegrationTest { } } - /** - - */ - /** * Asserts that an error is returned when a publish request to a node that is both * 'notification-only' as well as 'transient' contains an item element. * - *

    From XEP-0060 § 7.1.3.6:

    - *
    - * If the event type is notification + transient and the publisher provides an item, - * the service MUST bounce the publication request with a <bad-request/> error - * and a pubsub-specific error condition of <item-forbidden/>. - *
    - * * @throws NoResponseException if there was no response from the remote entity. * @throws XMPPErrorException if there was an XMPP error returned. * @throws NotConnectedException if the XMPP connection is not connected. @@ -104,7 +95,9 @@ public class PubSubIntegrationTest extends AbstractSmackIntegrationTest { * @see * 7.1.3.6 Request Does Not Match Configuration */ - @SmackIntegrationTest + @SmackIntegrationTest(section = "7.1.3.6", quote = + "If the event type is notification + transient and the publisher provides an item, the service MUST bounce " + + "the publication request with a error and a pubsub-specific error condition of .") public void transientNotificationOnlyNodeWithItemTest() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { final String nodename = "sinttest-transient-notificationonly-withitem-nodename-" + testRunId; final String itemId = "sinttest-transient-notificationonly-withitem-itemid-" + testRunId; @@ -120,7 +113,7 @@ public class PubSubIntegrationTest extends AbstractSmackIntegrationTest { // Add a dummy payload. If there is no payload, but just an item ID, then ejabberd will *not* return an error, // which I believe to be non-compliant behavior (although, granted, the XEP is not very clear about this). A user // which sends an empty item with ID to an node that is configured to be notification-only and transient probably - // does something wrong, as the item's ID will never appear anywhere. Hence it would be nice if the user would be + // does something wrong, as the item's ID will never appear anywhere. Hence, it would be nice if the user would be // made aware of this issue by returning an error. Sadly ejabberd does not do so. // See also https://github.com/processone/ejabberd/issues/2864#issuecomment-500741915 final StandardExtensionElement dummyPayload = StandardExtensionElement.builder("dummy-payload", @@ -132,9 +125,9 @@ public class PubSubIntegrationTest extends AbstractSmackIntegrationTest { Item item = new PayloadItem<>(itemId, dummyPayload); leafNode.publish(item); - }); - assertEquals(StanzaError.Type.MODIFY, e.getStanzaError().getType()); - assertNotNull(e.getStanzaError().getExtension("item-forbidden", "http://jabber.org/protocol/pubsub#errors")); + }, "Expected an error after publishing item " + itemId + " (but none occurred)."); + assertEquals(StanzaError.Type.MODIFY, e.getStanzaError().getType(), "Unexpected error type"); + assertNotNull(e.getStanzaError().getExtension("item-forbidden", "http://jabber.org/protocol/pubsub#errors"), "Expected error to contain 'item-forbidden', but it did not."); } finally { pubSubManagerOne.deleteNode(nodename); diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/softwareInfo/SoftwareInfoIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/softwareInfo/SoftwareInfoIntegrationTest.java index 3bf111f53..f01371f10 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/softwareInfo/SoftwareInfoIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/softwareInfo/SoftwareInfoIntegrationTest.java @@ -31,10 +31,13 @@ import org.jivesoftware.smackx.softwareinfo.form.SoftwareInfoForm; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; +import org.igniterealtime.smack.inttest.annotations.AfterClass; import org.igniterealtime.smack.inttest.annotations.BeforeClass; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil; +@SpecificationReference(document = "XEP-0232", version = "0.3") public class SoftwareInfoIntegrationTest extends AbstractSmackIntegrationTest { public final SoftwareInfoManager sim1; @@ -52,6 +55,11 @@ public class SoftwareInfoIntegrationTest extends AbstractSmackIntegrationTest { IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout); } + @AfterClass + public void cleanUp() throws Exception { + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); + } + @SmackIntegrationTest public void test() throws Exception { SoftwareInfoForm softwareInfoSent = createSoftwareInfoForm(); @@ -62,7 +70,8 @@ public class SoftwareInfoIntegrationTest extends AbstractSmackIntegrationTest { } }); SoftwareInfoForm softwareInfoFormReceived = sim2.fromJid(conOne.getUser()); - assertEquals(softwareInfoSent, softwareInfoFormReceived); + assertEquals(softwareInfoSent, softwareInfoFormReceived, + "Expected " + conOne.getUser() + "'s software version info as received by " + conTwo.getUser() + " to be equal to what " + conOne.getUser() + " publishes (but it is not)."); } private static SoftwareInfoForm createSoftwareInfoForm() throws URISyntaxException { diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java index 653bded72..e204031c4 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java @@ -17,7 +17,6 @@ package org.jivesoftware.smackx.usertune; import java.net.URI; -import java.util.concurrent.TimeoutException; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.NotLoggedInException; @@ -32,10 +31,12 @@ import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.annotations.AfterClass; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.annotations.SpecificationReference; import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil; import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; import org.junit.jupiter.api.Assertions; +@SpecificationReference(document = "XEP-0118", version = "1.3.0") public class UserTuneIntegrationTest extends AbstractSmackIntegrationTest { private final UserTuneManager utm1; @@ -97,6 +98,7 @@ public class UserTuneIntegrationTest extends AbstractSmackIntegrationTest { Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not."); } finally { unregisterListener(utm2, userTuneListener); + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); } } @@ -138,16 +140,10 @@ public class UserTuneIntegrationTest extends AbstractSmackIntegrationTest { registerListenerAndWait(utm2, ServiceDiscoveryManager.getInstanceFor(conTwo), userTuneListener); // Wait for the data to be received. - try { - Object result = userTuneReceived.waitForResult(timeout); - - // Explicitly assert the success case. - Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not."); - } catch (TimeoutException e) { - Assertions.fail("Expected to receive a PEP notification, but did not."); - } + assertResult(userTuneReceived, "Expected " + conTwo.getUser() + " to receive a PEP notification from " + conOne.getUser() + ", but did not."); } finally { unregisterListener(utm2, userTuneListener); + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/xdata/FormTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/xdata/FormTest.java index 64b4db597..9e127b85e 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/xdata/FormTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/xdata/FormTest.java @@ -128,28 +128,29 @@ public class FormTest extends AbstractSmackIntegrationTest { completedForm.setAnswer("time", true); completedForm.setAnswer("age", 20); // Create a new message to send with the completed form - msg2 = StanzaBuilder.buildMessage() + Message msg3 = StanzaBuilder.buildMessage() .to(conOne.getUser().asBareJid()) - .setThread(msg.getThread()) + .setThread(msg2.getThread()) .ofType(Message.Type.chat) .setBody("To enter a case please fill out this form and send it back to me") // Add the completed form to the message .addExtension(completedForm.getDataFormToSubmit()) .build(); // Send the message with the completed form - conTwo.sendStanza(msg2); + conTwo.sendStanza(msg3); // Get the message with the completed form - Message msg3 = collector.nextResult(); - assertNotNull(msg3, "Message not found"); + Message msg4 = collector.nextResult(); + assertNotNull(msg4, "Message not found"); // Retrieve the completed form - final DataForm completedForm2 = DataForm.from(msg3); + final DataForm completedForm2 = DataForm.from(msg4); assertNotNull(completedForm2); assertNotNull(completedForm2.getField("name")); assertNotNull(completedForm2.getField("description")); assertEquals( - completedForm2.getField("name").getValues().get(0).toString(), - "Credit card number invalid"); + "Credit card number invalid", + completedForm2.getField("name").getValues().get(0).toString() + ); assertNotNull(completedForm2.getField("time")); assertNotNull(completedForm2.getField("age")); assertEquals("20", completedForm2.getField("age").getValues().get(0).toString(), "The age is bad"); diff --git a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/ConfigurationTest.java b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/ConfigurationTest.java new file mode 100644 index 000000000..727b8a756 --- /dev/null +++ b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/ConfigurationTest.java @@ -0,0 +1,98 @@ +/** + * + * Copyright 2024 Guus der Kinderen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.igniterealtime.smack.inttest; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Verifies the functionality that's implemented in {@link Configuration}. + */ +public class ConfigurationTest { + @Test + public void testNormalizeXepUpperCaseNoSeperator() { + // Setup test fixture. + final String input = "XEP0001"; + + // Execute system under test. + final String output = Configuration.normalizeSpecification(input); + + // Verify results. + assertEquals("XEP0001", output); + } + + @Test + public void testNormalizeXepLowerCaseNoSeperator() { + // Setup test fixture. + final String input = "xep0001"; + + // Execute system under test. + final String output = Configuration.normalizeSpecification(input); + + // Verify results. + assertEquals("XEP0001", output); + } + + @Test + public void testNormalizeXepUpperCaseDash() { + // Setup test fixture. + final String input = "XEP-0001"; + + // Execute system under test. + final String output = Configuration.normalizeSpecification(input); + + // Verify results. + assertEquals("XEP0001", output); + } + + @Test + public void testNormalizeXepLowerCaseDash() { + // Setup test fixture. + final String input = "xep-0001"; + + // Execute system under test. + final String output = Configuration.normalizeSpecification(input); + + // Verify results. + assertEquals("XEP0001", output); + } + + @Test + public void testNormalizeXepUpperCaseSpace() { + // Setup test fixture. + final String input = "XEP 0001"; + + // Execute system under test. + final String output = Configuration.normalizeSpecification(input); + + // Verify results. + assertEquals("XEP0001", output); + } + + @Test + public void testNormalizeXepLowerCaseSpace() { + // Setup test fixture. + final String input = "xep 0001"; + + // Execute system under test. + final String output = Configuration.normalizeSpecification(input); + + // Verify results. + assertEquals("XEP0001", output); + } +} diff --git a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFrameWorkTest.java b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFrameWorkTest.java index 48b827770..fbdfa8cef 100644 --- a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFrameWorkTest.java +++ b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFrameWorkTest.java @@ -31,26 +31,26 @@ import org.junit.jupiter.api.Test; public class SmackIntegrationTestFrameWorkTest { private static class ValidLowLevelList { - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "MethodCanBeStatic"}) public void test(List connections) { } } private static class InvalidLowLevelList { - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "MethodCanBeStatic"}) public void test(List connections, boolean invalid) { } } private static class ValidLowLevelVarargs { - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "MethodCanBeStatic"}) public void test(AbstractXMPPConnection connectionOne, AbstractXMPPConnection connectionTwo, AbstractXMPPConnection connectionThree) { } } private static class InvalidLowLevelVarargs { - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "MethodCanBeStatic"}) public void test(AbstractXMPPConnection connectionOne, Integer invalid, AbstractXMPPConnection connectionTwo, AbstractXMPPConnection connectionThree) { } @@ -97,7 +97,7 @@ public class SmackIntegrationTestFrameWorkTest { } private static class ValidUnconnectedConnectionSource { - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "MethodCanBeStatic"}) public void test(AbstractSmackLowLevelIntegrationTest.UnconnectedConnectionSource source) { } } diff --git a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestXmppConnectionManagerTest.java b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestXmppConnectionManagerTest.java index 7a2de6097..d8aa16015 100644 --- a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestXmppConnectionManagerTest.java +++ b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestXmppConnectionManagerTest.java @@ -43,7 +43,7 @@ public class SmackIntegrationTestXmppConnectionManagerTest { ModularXmppClientToServerConnection.class, ModularXmppClientToServerConnectionConfiguration.class, ModularXmppClientToServerConnectionConfiguration.Builder.class) - .applyExtraConfguration(b -> b.removeAllModules().addModule(XmppTcpTransportModuleDescriptor.class)) + .applyExtraConfiguration(b -> b.removeAllModules().addModule(XmppTcpTransportModuleDescriptor.class)) .build(); Configuration sinttestConfiguration = Configuration.builder().setService("example.org").build(); diff --git a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/util/MultiResultSyncPointTest.java b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/util/MultiResultSyncPointTest.java new file mode 100644 index 000000000..5a4db37c4 --- /dev/null +++ b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/util/MultiResultSyncPointTest.java @@ -0,0 +1,108 @@ +/** + * + * Copyright 2024 Guus der Kinderen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.igniterealtime.smack.inttest.util; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.List; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.TimeoutException; + +import org.jivesoftware.smack.util.Async; + +import org.junit.jupiter.api.Test; + +public class MultiResultSyncPointTest { + @Test + public void testResultSyncPoint() throws Exception { + final String result1 = "r1"; + final String result2 = "r2"; + final CyclicBarrier barrier = new CyclicBarrier(2); + final MultiResultSyncPoint rsp = new MultiResultSyncPoint<>(2); + Async.go(new Async.ThrowingRunnable() { + @Override + public void runOrThrow() throws InterruptedException, BrokenBarrierException { + barrier.await(); + rsp.signal(result1); + rsp.signal(result2); + } + }); + barrier.await(); + List receivedResult = rsp.waitForResults(60 * 1000); + assertTrue(receivedResult.contains(result1)); + assertTrue(receivedResult.contains(result2)); + } + + @Test + public void exceptionTestResultSyncPoint() throws Exception { + final CyclicBarrier barrier = new CyclicBarrier(2); + final ResultSyncPoint rsp = new ResultSyncPoint<>(); + Async.go(new Async.ThrowingRunnable() { + @Override + public void runOrThrow() throws InterruptedException, BrokenBarrierException { + barrier.await(); + rsp.signal(new MultiResultSyncPointTest.TestException()); + } + }); + barrier.await(); + assertThrows(MultiResultSyncPointTest.TestException.class, () -> rsp.waitForResult(60 * 1000)); + } + + @Test + public void testTimeout() throws Exception { + final MultiResultSyncPoint rsp = new MultiResultSyncPoint<>(2); + try { + rsp.waitForResults(100); + fail("A timeout exception should have been thrown."); + } catch (TimeoutException e) { + // Expected + } + } + + @Test + public void testTimeoutWithOneResult() throws Exception { + final String result1 = "partial"; + final CyclicBarrier barrier = new CyclicBarrier(2); + final MultiResultSyncPoint rsp = new MultiResultSyncPoint<>(2); + Async.go(new Async.ThrowingRunnable() { + @Override + public void runOrThrow() throws InterruptedException, BrokenBarrierException { + barrier.await(); + rsp.signal(result1); + } + }); + barrier.await(); + try { + rsp.waitForResults(100); + fail("A timeout exception should have been thrown."); + } catch (TimeoutException e) { + // Expected + } + } + + private static class TestException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1L; + + } +} diff --git a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/util/ResultSyncPointTest.java b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/util/ResultSyncPointTest.java index 589880204..c6e22bd72 100644 --- a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/util/ResultSyncPointTest.java +++ b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/util/ResultSyncPointTest.java @@ -18,9 +18,11 @@ package org.igniterealtime.smack.inttest.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.TimeoutException; import org.jivesoftware.smack.util.Async; @@ -60,6 +62,17 @@ public class ResultSyncPointTest { assertThrows(TestException.class, () -> rsp.waitForResult(60 * 1000)); } + @Test + public void testTimeout() throws Exception { + final MultiResultSyncPoint rsp = new MultiResultSyncPoint<>(2); + try { + rsp.waitForResults(100); + fail("A timeout exception should have been thrown."); + } catch (TimeoutException e) { + // Expected + } + } + private static class TestException extends Exception { /** diff --git a/smack-java8-full/build.gradle b/smack-java11-full/build.gradle similarity index 91% rename from smack-java8-full/build.gradle rename to smack-java11-full/build.gradle index 7607d3c02..4f4bf0652 100644 --- a/smack-java8-full/build.gradle +++ b/smack-java11-full/build.gradle @@ -1,3 +1,7 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' +} + description = """\ Full Smack library for Java SE.""" @@ -6,7 +10,7 @@ dependencies { api project(':smack-debug') api project(':smack-experimental') api project(':smack-extensions') - api project(':smack-java8') + api project(':smack-java11') api project(':smack-legacy') api project(':smack-omemo') api project(':smack-openpgp') @@ -44,6 +48,7 @@ task convertModularXmppClientToServerConnectionStateGraphDotToPng(type: Exec) { executable 'dot' args "-Tpng", "-o", "${outputs.files.first()}", "${inputs.files.first()}" } +copyJavadocDocFiles.dependsOn convertModularXmppClientToServerConnectionStateGraphDotToPng task cleanGenerateFiles(type: Delete) { delete 'src/javadoc/org/jivesoftware/smack/full/doc-files/ModularXmppClientToServerConnectionStateGraph.dot', 'src/javadoc/org/jivesoftware/smack/full/doc-files/ModularXmppClientToServerConnectionStateGraph.png' diff --git a/smack-java8-full/src/javadoc/org/jivesoftware/smack/full/doc-files/.gitignore b/smack-java11-full/src/javadoc/org/jivesoftware/smack/full/doc-files/.gitignore similarity index 100% rename from smack-java8-full/src/javadoc/org/jivesoftware/smack/full/doc-files/.gitignore rename to smack-java11-full/src/javadoc/org/jivesoftware/smack/full/doc-files/.gitignore diff --git a/smack-java8-full/src/main/java/org/jivesoftware/smack/full/ModularXmppClientToServerConnectionTool.java b/smack-java11-full/src/main/java/org/jivesoftware/smack/full/ModularXmppClientToServerConnectionTool.java similarity index 100% rename from smack-java8-full/src/main/java/org/jivesoftware/smack/full/ModularXmppClientToServerConnectionTool.java rename to smack-java11-full/src/main/java/org/jivesoftware/smack/full/ModularXmppClientToServerConnectionTool.java diff --git a/smack-java8-full/src/main/java/org/jivesoftware/smack/full/package-info.java b/smack-java11-full/src/main/java/org/jivesoftware/smack/full/package-info.java similarity index 100% rename from smack-java8-full/src/main/java/org/jivesoftware/smack/full/package-info.java rename to smack-java11-full/src/main/java/org/jivesoftware/smack/full/package-info.java diff --git a/smack-java8-full/src/main/java/org/jivesoftware/smackx/SmackExtensions.java b/smack-java11-full/src/main/java/org/jivesoftware/smackx/SmackExtensions.java similarity index 94% rename from smack-java8-full/src/main/java/org/jivesoftware/smackx/SmackExtensions.java rename to smack-java11-full/src/main/java/org/jivesoftware/smackx/SmackExtensions.java index 8ec3ad057..186562bba 100644 --- a/smack-java8-full/src/main/java/org/jivesoftware/smackx/SmackExtensions.java +++ b/smack-java11-full/src/main/java/org/jivesoftware/smackx/SmackExtensions.java @@ -18,7 +18,7 @@ package org.jivesoftware.smackx; /** * This is just a dummy class, please head over to {@link org.jivesoftware.smackx} for more information on Smack - * extensions. The dummy class causes javadoc generate the HTML for smackx.pacakge-info.java, which would otherwise be + * extensions. The dummy class causes javadoc generate the HTML for smackx.package-info.java, which would otherwise be * not generated, as org.jivesoftware.smackx is an empty package (see * JDK-4492654). */ diff --git a/smack-java8-full/src/main/java/org/jivesoftware/smackx/package-info.java b/smack-java11-full/src/main/java/org/jivesoftware/smackx/package-info.java similarity index 98% rename from smack-java8-full/src/main/java/org/jivesoftware/smackx/package-info.java rename to smack-java11-full/src/main/java/org/jivesoftware/smackx/package-info.java index d9f01636e..0769f65ba 100644 --- a/smack-java8-full/src/main/java/org/jivesoftware/smackx/package-info.java +++ b/smack-java11-full/src/main/java/org/jivesoftware/smackx/package-info.java @@ -71,7 +71,7 @@ * Extended Stanza Addressing * XEP-0033 * - * Allows to include headers in stanzas in order to specifiy multiple recipients or sub-addresses. + * Allows to include headers in stanzas in order to specify multiple recipients or sub-addresses. * * * Multi User Chat @@ -477,7 +477,7 @@ * Data Forms Geolocation Element * XEP-0350 * - * Allows to include XEP-0080 gelocation data in XEP-0004 data forms. + * Allows to include XEP-0080 geolocation data in XEP-0004 data forms. * * * Client State Indication @@ -511,7 +511,7 @@ * * * References - * XEP-0372 + * XEP-0372 * * Add references like mentions or external data to stanzas. * diff --git a/smack-java8-full/src/test/java/org/jivesoftware/smack/full/ExtensionElementQNameDeclaredTest.java b/smack-java11-full/src/test/java/org/jivesoftware/smack/full/ExtensionElementQNameDeclaredTest.java similarity index 100% rename from smack-java8-full/src/test/java/org/jivesoftware/smack/full/ExtensionElementQNameDeclaredTest.java rename to smack-java11-full/src/test/java/org/jivesoftware/smack/full/ExtensionElementQNameDeclaredTest.java diff --git a/smack-java8-full/src/test/java/org/jivesoftware/smack/full/ModularXmppClientToServerConnectionStateGraphTest.java b/smack-java11-full/src/test/java/org/jivesoftware/smack/full/ModularXmppClientToServerConnectionStateGraphTest.java similarity index 100% rename from smack-java8-full/src/test/java/org/jivesoftware/smack/full/ModularXmppClientToServerConnectionStateGraphTest.java rename to smack-java11-full/src/test/java/org/jivesoftware/smack/full/ModularXmppClientToServerConnectionStateGraphTest.java diff --git a/smack-java8-full/src/test/resources/state-graph.dot b/smack-java11-full/src/test/resources/state-graph.dot similarity index 100% rename from smack-java8-full/src/test/resources/state-graph.dot rename to smack-java11-full/src/test/resources/state-graph.dot diff --git a/smack-java8/build.gradle b/smack-java11/build.gradle similarity index 67% rename from smack-java8/build.gradle rename to smack-java11/build.gradle index d26aa5ce3..d3d157ad1 100644 --- a/smack-java8/build.gradle +++ b/smack-java11/build.gradle @@ -1,7 +1,11 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' +} + description = """\ -Smack for Java7 (or higher). +Smack for Java 11 (or higher). This is a pseudo-artifact that pulls all the required dependencies to -run Smack on Java 7 (or higher) JVMs. Usually you want to add additional +run Smack on Java 11 (or higher) JVMs. Usually you want to add additional dependencies to smack-tcp, smack-extensions and smack-experimental.""" dependencies { diff --git a/smack-java8/src/main/java/org/jivesoftware/smack/java7/Java7SmackInitializer.java b/smack-java11/src/main/java/org/jivesoftware/smack/java7/Java7SmackInitializer.java similarity index 100% rename from smack-java8/src/main/java/org/jivesoftware/smack/java7/Java7SmackInitializer.java rename to smack-java11/src/main/java/org/jivesoftware/smack/java7/Java7SmackInitializer.java diff --git a/smack-java8/src/main/java/org/jivesoftware/smack/java7/XmppHostnameVerifier.java b/smack-java11/src/main/java/org/jivesoftware/smack/java7/XmppHostnameVerifier.java similarity index 98% rename from smack-java8/src/main/java/org/jivesoftware/smack/java7/XmppHostnameVerifier.java rename to smack-java11/src/main/java/org/jivesoftware/smack/java7/XmppHostnameVerifier.java index 16e1c5668..a03cd097a 100644 --- a/smack-java8/src/main/java/org/jivesoftware/smack/java7/XmppHostnameVerifier.java +++ b/smack-java11/src/main/java/org/jivesoftware/smack/java7/XmppHostnameVerifier.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015 Florian Schmaus + * Copyright 2015-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,8 @@ import java.security.Principal; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.logging.Level; @@ -135,7 +135,7 @@ public class XmppHostnameVerifier implements HostnameVerifier { private static void matchDns(String name, X509Certificate cert) throws CertificateException { Collection> subjAltNames = cert.getSubjectAlternativeNames(); if (subjAltNames != null) { - List nonMatchingDnsAltnames = new LinkedList<>(); + List nonMatchingDnsAltnames = new ArrayList<>(); for (List san : subjAltNames) { if (((Integer) san.get(0)).intValue() != ALTNAME_DNS) { continue; @@ -253,7 +253,7 @@ public class XmppHostnameVerifier implements HostnameVerifier { if (subjectAlternativeNames == null) { throw new CertificateException("No subject alternative names present"); } - List nonMatchingIpAltnames = new LinkedList<>(); + List nonMatchingIpAltnames = new ArrayList<>(); for (List san : subjectAlternativeNames) { if (((Integer) san.get(0)).intValue() != ALTNAME_IP) { continue; diff --git a/smack-java8/src/main/java/org/jivesoftware/smack/java7/package-info.java b/smack-java11/src/main/java/org/jivesoftware/smack/java7/package-info.java similarity index 100% rename from smack-java8/src/main/java/org/jivesoftware/smack/java7/package-info.java rename to smack-java11/src/main/java/org/jivesoftware/smack/java7/package-info.java diff --git a/smack-java8/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64Encoder.java b/smack-java11/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64Encoder.java similarity index 100% rename from smack-java8/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64Encoder.java rename to smack-java11/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64Encoder.java diff --git a/smack-java8/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64UrlSafeEncoder.java b/smack-java11/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64UrlSafeEncoder.java similarity index 100% rename from smack-java8/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64UrlSafeEncoder.java rename to smack-java11/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64UrlSafeEncoder.java diff --git a/smack-java8/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/package-info.java b/smack-java11/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/package-info.java similarity index 100% rename from smack-java8/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/package-info.java rename to smack-java11/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/package-info.java diff --git a/smack-java8/src/main/java/org/jivesoftware/smack/util/stringencoder/package-info.java b/smack-java11/src/main/java/org/jivesoftware/smack/util/stringencoder/package-info.java similarity index 100% rename from smack-java8/src/main/java/org/jivesoftware/smack/util/stringencoder/package-info.java rename to smack-java11/src/main/java/org/jivesoftware/smack/util/stringencoder/package-info.java diff --git a/smack-jingle-old/build.gradle b/smack-jingle-old/build.gradle index 5651864f2..63e732bee 100644 --- a/smack-jingle-old/build.gradle +++ b/smack-jingle-old/build.gradle @@ -1,3 +1,7 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' +} + description = """\ Smack Jingle API. Warning: This API is beta, outdated and currenlty unmaintained.""" diff --git a/smack-jingle-old/src/integration-test/java/org/jivesoftware/smackx/jingle/nat/STUNResolverTest.java b/smack-jingle-old/src/integration-test/java/org/jivesoftware/smackx/jingle/nat/STUNResolverTest.java index 5d9425a92..febac9374 100644 --- a/smack-jingle-old/src/integration-test/java/org/jivesoftware/smackx/jingle/nat/STUNResolverTest.java +++ b/smack-jingle-old/src/integration-test/java/org/jivesoftware/smackx/jingle/nat/STUNResolverTest.java @@ -99,7 +99,7 @@ public class STUNResolverTest extends SmackTestCase { stunResolver.addCandidate(cand3); stunResolver.addCandidate(cand4); - assertEquals(stunResolver.getPreferredCandidate(), candH); + assertEquals(candH, stunResolver.getPreferredCandidate()); } /** @@ -127,7 +127,7 @@ public class STUNResolverTest extends SmackTestCase { iceResolver.addCandidate(cand3); iceResolver.addCandidate(cand4); - assertEquals(iceResolver.getPreferredCandidate(), candH); + assertEquals(candH, iceResolver.getPreferredCandidate()); } /** diff --git a/smack-jingle-old/src/integration-test/java/org/jivesoftware/smackx/jingle/nat/TransportCandidateTest.java b/smack-jingle-old/src/integration-test/java/org/jivesoftware/smackx/jingle/nat/TransportCandidateTest.java index 36830e911..763d52877 100644 --- a/smack-jingle-old/src/integration-test/java/org/jivesoftware/smackx/jingle/nat/TransportCandidateTest.java +++ b/smack-jingle-old/src/integration-test/java/org/jivesoftware/smackx/jingle/nat/TransportCandidateTest.java @@ -65,7 +65,7 @@ public class TransportCandidateTest extends SmackTestCase { candList.add(cand4); Collections.sort(candList); - assertEquals(candList.get(candList.size() - 1), candH); + assertEquals(candH, candList.get(candList.size() - 1)); } protected int getMaxConnections() { diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleManager.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleManager.java index acd9a5796..f4ad1c0a8 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleManager.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleManager.java @@ -540,7 +540,7 @@ public class JingleManager implements JingleSessionListener { // } /** * When the session request is acceptable, this method should be invoked. It - * will create an JingleSession which allows the negotiation to procede. + * will create an JingleSession which allows the negotiation to proceed. * * @param request the remote request that is being accepted. * @return the session which manages the rest of the negotiation. @@ -560,7 +560,7 @@ public class JingleManager implements JingleSessionListener { /** * When the session request is acceptable, this method should be invoked. It - * will create an JingleSession which allows the negotiation to procede. + * will create an JingleSession which allows the negotiation to proceed. * This method use JingleMediaManager to select the supported Payload types. * * @param request the remote request that is being accepted. diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleNegotiator.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleNegotiator.java index 656faad25..d944679b8 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleNegotiator.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleNegotiator.java @@ -221,7 +221,7 @@ public abstract class JingleNegotiator { * <transport> * * This way, each segment of a Jingle stanza has a corresponding negotiator that know how to deal with that - * part of the Jingle packet. It also allows us to support Jingle packets of arbitraty complexity. + * part of the Jingle packet. It also allows us to support Jingle packets of arbitrary complexity. * * Each parent calls dispatchIncomingPacket for each of its children. The children then pass back a List of * results that will get sent when we reach the top level negotiator (JingleSession). diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSession.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSession.java index dabc962b0..cb141e837 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSession.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSession.java @@ -431,7 +431,7 @@ public final class JingleSession extends JingleNegotiator implements MediaReceiv /** * Complete and send a packet. Complete all the null fields in a Jingle - * reponse, using the session information we have. + * response, using the session information we have. * * @param jout * the Jingle stanza we want to complete and send @@ -445,7 +445,7 @@ public final class JingleSession extends JingleNegotiator implements MediaReceiv /** * Complete and send a packet. Complete all the null fields in a Jingle - * reponse, using the session information we have or some info from the + * response, using the session information we have or some info from the * incoming packet. * * @param iq The Jingle stanza we are responding to @@ -1097,7 +1097,7 @@ public final class JingleSession extends JingleNegotiator implements MediaReceiv } /** - * This is the starting point for intitiating a new session. + * This is the starting point for initiating a new session. * * @throws IllegalStateException if an illegal state was encountered * @throws SmackException if Smack detected an exceptional situation. diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSessionState.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSessionState.java index a1bc7263b..5a1f7660a 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSessionState.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSessionState.java @@ -56,7 +56,7 @@ public abstract class JingleSessionState { /** * Process an incoming Jingle Packet. - * When you look at the GoF State pattern this method roughly corresponds to example on p310: ProcessOctect() + * When you look at the GoF State pattern this method roughly corresponds to example on p310: ProcessOctet() * * @param session the jingle session. * @param jingle the jingle stanza. diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/listeners/CreatedJingleSessionListener.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/listeners/CreatedJingleSessionListener.java index 294e321cd..dda67f72e 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/listeners/CreatedJingleSessionListener.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/listeners/CreatedJingleSessionListener.java @@ -19,7 +19,7 @@ package org.jivesoftware.smackx.jingleold.listeners; import org.jivesoftware.smackx.jingleold.JingleSession; /** - * Inteface used to dispatch a event when a Jingle session is created. + * Interface used to dispatch an event when a Jingle session is created. * * @author Thiago Camargo */ diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/listeners/JingleSessionListener.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/listeners/JingleSessionListener.java index fb754e489..768cc42c9 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/listeners/JingleSessionListener.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/listeners/JingleSessionListener.java @@ -33,7 +33,7 @@ public interface JingleSessionListener extends JingleListener { * Notification that the session has been established. Arguments specify * the payload type and transport to use. * - * @param pt the Payload tyep to use + * @param pt the Payload type to use * @param remoteCandidate the remote candidate to use for connecting to the remote * service. * @param localCandidate the local candidate where we must listen for connections diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/media/JingleMediaSession.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/media/JingleMediaSession.java index 2956a265a..7df3c2f06 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/media/JingleMediaSession.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/media/JingleMediaSession.java @@ -25,7 +25,7 @@ import org.jivesoftware.smackx.jingleold.nat.TransportCandidate; /** * Public Abstract Class provides a clear interface between Media Session and Jingle API. *

    - * When a Jingle Session is fully stablished, we will have a Payload Type and two transport candidates defined for it. + * When a Jingle Session is fully established, we will have a Payload Type and two transport candidates defined for it. * Smack Jingle API don't implement Media Transmit and Receive methods. * But provides an interface to let the user implements it using another API. For instance: JMF. *

    @@ -153,7 +153,7 @@ public abstract class JingleMediaSession { public abstract void startReceive(); /** - * Set transmit activity. If the active is true, the instance should trasmit. + * Set transmit activity. If the active is true, the instance should transmit. * If it is set to false, the instance should pause transmit. * * @param active TODO javadoc me please @@ -173,7 +173,7 @@ public abstract class JingleMediaSession { /** * Called when new Media is received. * - * @param participant the particpant. + * @param participant the participant. */ public void mediaReceived(String participant) { for (MediaReceivedListener mediaReceivedListener : mediaReceivedListeners) { diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/media/PayloadType.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/media/PayloadType.java index c7dd7b8e0..08675d8b4 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/media/PayloadType.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/media/PayloadType.java @@ -322,7 +322,7 @@ public class PayloadType { } /** - * Set tha sampling clockRate for a playload type. + * Set tha sampling clockRate for a payload type. * * @param rate The sampling clockRate */ diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/JMFInit.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/JMFInit.java index ed86a85de..c86734cc2 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/JMFInit.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/JMFInit.java @@ -43,6 +43,7 @@ public class JMFInit extends Frame implements Runnable { private boolean visible = false; + @SuppressWarnings({"this-escape", "DoNotCall"}) public JMFInit(String[] args, boolean visible) { super("Initializing JMF..."); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/jmf/AudioChannel.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/jmf/AudioChannel.java index 88cb3a649..61340f80e 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/jmf/AudioChannel.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/jmf/AudioChannel.java @@ -322,7 +322,7 @@ public class AudioChannel { String encoding = codecFormat.getEncoding(); if (encoding.equalsIgnoreCase(AudioFormat.GSM) || encoding.equalsIgnoreCase(AudioFormat.GSM_RTP)) { - return milliseconds * 4; // 1 byte per millisec + return milliseconds * 4; // 1 byte per millisecond } else if (encoding.equalsIgnoreCase(AudioFormat.ULAW) || encoding.equalsIgnoreCase(AudioFormat.ULAW_RTP)) { diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/jmf/AudioMediaSession.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/jmf/AudioMediaSession.java index 42525c68d..22013df8b 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/jmf/AudioMediaSession.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/jmf/AudioMediaSession.java @@ -53,6 +53,7 @@ public class AudioMediaSession extends JingleMediaSession { * @param locator media locator * @param jingleSession the jingle session. */ + @SuppressWarnings("this-escape") public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, String locator, JingleSession jingleSession) { super(payloadType, remote, local, locator == null ? "dsound://" : locator, jingleSession); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/jspeex/AudioMediaSession.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/jspeex/AudioMediaSession.java index 555836732..2874327ca 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/jspeex/AudioMediaSession.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/jspeex/AudioMediaSession.java @@ -71,17 +71,17 @@ public class AudioMediaSession extends JingleMediaSession implements MediaSessio * @throws NoProcessorException if there is no media processor. * @throws UnsupportedFormatException if the format is not supported. * @throws IOException if an I/O error occurred. - * @throws GeneralSecurityException if there was a geneeral security exception. + * @throws GeneralSecurityException if there was a general security exception. */ public static MediaSession createSession(String localhost, int localPort, String remoteHost, int remotePort, MediaSessionListener eventHandler, int quality, boolean secure, boolean micOn) throws NoProcessorException, UnsupportedFormatException, IOException, GeneralSecurityException { SpeexFormat.setFramesPerPacket(1); - /** + /* * The master key. Hardcoded for now. */ byte[] masterKey = new byte[] {(byte) 0xE1, (byte) 0xF9, 0x7A, 0x0D, 0x3E, 0x01, (byte) 0x8B, (byte) 0xE0, (byte) 0xD6, 0x4F, (byte) 0xA3, 0x2C, 0x06, (byte) 0xDE, 0x41, 0x39}; - /** + /* * The master salt. Hardcoded for now. */ byte[] masterSalt = new byte[] {0x0E, (byte) 0xC6, 0x75, (byte) 0xAD, 0x49, (byte) 0x8A, (byte) 0xFE, (byte) 0xEB, (byte) 0xB6, (byte) 0x96, 0x0B, 0x3A, (byte) 0xAB, (byte) 0xE6}; @@ -104,6 +104,7 @@ public class AudioMediaSession extends JingleMediaSession implements MediaSessio * @param locator media locator * @param jingleSession the jingle session. */ + @SuppressWarnings("this-escape") public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, String locator, JingleSession jingleSession) { super(payloadType, remote, local, locator == null ? "dsound://" : locator, jingleSession); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/sshare/ScreenShareSession.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/sshare/ScreenShareSession.java index 8a33211e0..2cae0fc22 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/sshare/ScreenShareSession.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/sshare/ScreenShareSession.java @@ -66,6 +66,7 @@ public class ScreenShareSession extends JingleMediaSession { * @param locator media locator * @param jingleSession the jingle session. */ + @SuppressWarnings("this-escape") public ScreenShareSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final String locator, JingleSession jingleSession) { super(payloadType, remote, local, "Screen", jingleSession); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/sshare/api/ImageReceiver.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/sshare/api/ImageReceiver.java index c3642d6dd..ed3010be7 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/sshare/api/ImageReceiver.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/sshare/api/ImageReceiver.java @@ -48,6 +48,7 @@ public class ImageReceiver extends Canvas { private int remotePort; private ImageDecoder decoder; + @SuppressWarnings("this-escape") public ImageReceiver(final InetAddress remoteHost, final int remotePort, final int localPort, int width, int height) { tiles = new BufferedImage[width][height]; diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/sshare/api/OctTreeQuantizer.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/sshare/api/OctTreeQuantizer.java index bb6e7ff27..cff438024 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/sshare/api/OctTreeQuantizer.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/sshare/api/OctTreeQuantizer.java @@ -76,6 +76,7 @@ public class OctTreeQuantizer implements Quantizer { private int colors = 0; private final List> colorList; + @SuppressWarnings({"JdkObsolete", "this-escape"}) public OctTreeQuantizer() { setup(256); colorList = new ArrayList<>(MAX_LEVEL + 1); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/test/TestMediaSession.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/test/TestMediaSession.java index 1bb4cf746..edfa5a83a 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/test/TestMediaSession.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/mediaimpl/test/TestMediaSession.java @@ -37,6 +37,7 @@ public class TestMediaSession extends JingleMediaSession { * @param locator media locator * @param jingleSession the jingle session. */ + @SuppressWarnings("this-escape") public TestMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, final String locator, JingleSession jingleSession) { super(payloadType, remote, local, "Test", jingleSession); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/FixedResolver.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/FixedResolver.java index d1ea13fad..0a21ac4aa 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/FixedResolver.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/FixedResolver.java @@ -38,6 +38,7 @@ public class FixedResolver extends TransportResolver { * @param ip the IP address. * @param port the port number. */ + @SuppressWarnings("this-escape") public FixedResolver(String ip, int port) { super(); setFixedCandidate(ip, port); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/ICEResolver.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/ICEResolver.java index f286f5f38..95fce2b6c 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/ICEResolver.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/ICEResolver.java @@ -56,6 +56,7 @@ public class ICEResolver extends TransportResolver { static Map negociatorsMap = new HashMap<>(); // ICENegociator iceNegociator = null; + @SuppressWarnings("this-escape") public ICEResolver(XMPPConnection connection, String server, int port) { super(); this.connection = connection; diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/STUNResolver.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/STUNResolver.java index b0b010f81..7eb49df5e 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/STUNResolver.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/STUNResolver.java @@ -23,6 +23,7 @@ import java.net.SocketException; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -135,7 +136,7 @@ public class STUNResolver extends TransportResolver { * @param stunConfigStream An InputStream with the configuration file. * @return A list of loaded servers */ - public ArrayList loadSTUNServers(java.io.InputStream stunConfigStream) { + public List loadSTUNServers(java.io.InputStream stunConfigStream) { ArrayList serversList = new ArrayList<>(); String serverName; int serverPort; @@ -211,7 +212,7 @@ public class STUNResolver extends TransportResolver { * * @return a list of services */ - public ArrayList loadSTUNServers() { + public List loadSTUNServers() { ArrayList serversList = new ArrayList<>(); // Load the STUN configuration @@ -248,7 +249,7 @@ public class STUNResolver extends TransportResolver { * * @return the best STUN server that can be used. */ - private static STUNService bestSTUNServer(ArrayList listServers) { + private static STUNService bestSTUNServer(List listServers) { if (listServers.isEmpty()) { return null; } else { diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TcpUdpBridgeClient.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TcpUdpBridgeClient.java index 8e3936719..3451b7c1c 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TcpUdpBridgeClient.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TcpUdpBridgeClient.java @@ -45,6 +45,7 @@ public class TcpUdpBridgeClient { private DatagramSocket localUdpSocket; private Socket localTcpSocket; + @SuppressWarnings("this-escape") public TcpUdpBridgeClient(String remoteTcpHost, String remoteUdpHost, int remoteTcpPort, int remoteUdpPort) { this.remoteTcpHost = remoteTcpHost; this.remoteUdpHost = remoteUdpHost; diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TcpUdpBridgeServer.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TcpUdpBridgeServer.java index 2ff8744b5..d5abc6bee 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TcpUdpBridgeServer.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TcpUdpBridgeServer.java @@ -48,6 +48,7 @@ public class TcpUdpBridgeServer { private Socket localTcpSocket; private ServerSocket serverTcpSocket; + @SuppressWarnings("this-escape") public TcpUdpBridgeServer(String remoteTcpHost, String remoteUdpHost, int remoteTcpPort, int remoteUdpPort) { this.remoteTcpHost = remoteTcpHost; this.remoteUdpHost = remoteUdpHost; diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportCandidate.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportCandidate.java index 103286862..a9f3f7f8a 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportCandidate.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportCandidate.java @@ -17,13 +17,13 @@ package org.jivesoftware.smackx.jingleold.nat; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -661,21 +661,13 @@ public abstract class TransportCandidate { String local = id.substring(0, keySplitIndex) + ";" + localUser; String remote = id.substring(keySplitIndex) + ";" + remoteUser; - try { - if (session.getConnection().getUser().equals(session.getInitiator())) { - - this.send = local.getBytes("UTF-8"); - this.receive = remote.getBytes("UTF-8"); - } else { - this.receive = local.getBytes("UTF-8"); - this.send = remote.getBytes("UTF-8"); - } + if (session.getConnection().getUser().equals(session.getInitiator())) { + this.send = local.getBytes(StandardCharsets.UTF_8); + this.receive = remote.getBytes(StandardCharsets.UTF_8); + } else { + this.receive = local.getBytes(StandardCharsets.UTF_8); + this.send = remote.getBytes(StandardCharsets.UTF_8); } - catch (UnsupportedEncodingException e) { - LOGGER.log(Level.WARNING, "exception", e); - } - - } @SuppressWarnings("UnusedVariable") @@ -706,7 +698,7 @@ public abstract class TransportCandidate { long delay = 100 / replyTries; - String[] str = new String(packet.getData(), "UTF-8").split(";"); + String[] str = new String(packet.getData(), StandardCharsets.UTF_8).split(";"); String pass = str[0]; String[] address = str[1].split(":"); String ip = address[0]; @@ -714,13 +706,7 @@ public abstract class TransportCandidate { if (pass.equals(candidate.getPassword()) && !accept) { - byte[] cont = null; - try { - cont = (password + ";" + candidate.getIp() + ":" + candidate.getPort()).getBytes("UTF-8"); - } - catch (UnsupportedEncodingException e) { - LOGGER.log(Level.WARNING, "exception", e); - } + byte[] cont = (password + ";" + candidate.getIp() + ":" + candidate.getPort()).getBytes(StandardCharsets.UTF_8); packet.setData(cont); packet.setLength(cont.length); @@ -778,31 +764,24 @@ public abstract class TransportCandidate { DatagramListener listener = new DatagramListener() { @Override public boolean datagramReceived(DatagramPacket datagramPacket) { + LOGGER.fine("ECHO Received to: " + candidate.getIp() + ":" + candidate.getPort() + " data: " + new String(datagramPacket.getData(), StandardCharsets.UTF_8)); + String[] str = new String(datagramPacket.getData(), StandardCharsets.UTF_8).split(";"); + String pass = str[0]; + String[] addr = str[1].split(":"); + String ip = addr[0]; + String pt = addr[1]; - try { - LOGGER.fine("ECHO Received to: " + candidate.getIp() + ":" + candidate.getPort() + " data: " + new String(datagramPacket.getData(), "UTF-8")); - String[] str = new String(datagramPacket.getData(), "UTF-8").split(";"); - String pass = str[0]; - String[] addr = str[1].split(":"); - String ip = addr[0]; - String pt = addr[1]; - - // CHECKSTYLE:OFF - if (pass.equals(password) - && transportCandidate.getIp().indexOf(ip) != -1 - && transportCandidate.getPort() == Integer.parseInt(pt)) { - // CHECKSTYLE:ON - LOGGER.fine("ECHO OK: " + candidate.getIp() + ":" + candidate.getPort() + " <-> " + transportCandidate.getIp() + ":" + transportCandidate.getPort()); - TestResult testResult = new TestResult(); - testResult.setResult(true); - ended = true; - fireTestResult(testResult, transportCandidate); - return true; - } - - } - catch (UnsupportedEncodingException e) { - LOGGER.log(Level.WARNING, "exception", e); + // CHECKSTYLE:OFF + if (pass.equals(password) + && transportCandidate.getIp().indexOf(ip) != -1 + && transportCandidate.getPort() == Integer.parseInt(pt)) { + // CHECKSTYLE:ON + LOGGER.fine("ECHO OK: " + candidate.getIp() + ":" + candidate.getPort() + " <-> " + transportCandidate.getIp() + ":" + transportCandidate.getPort()); + TestResult testResult = new TestResult(); + testResult.setResult(true); + ended = true; + fireTestResult(testResult, transportCandidate); + return true; } LOGGER.fine("ECHO Wrong Data: " + datagramPacket.getAddress().getHostAddress() + ":" + datagramPacket.getPort()); @@ -812,13 +791,7 @@ public abstract class TransportCandidate { addListener(listener); - byte[] content = null; - try { - content = new String(password + ";" + getIp() + ":" + getPort()).getBytes("UTF-8"); - } - catch (UnsupportedEncodingException e) { - LOGGER.log(Level.WARNING, "exception", e); - } + byte[] content = new String(password + ";" + getIp() + ":" + getPort()).getBytes(StandardCharsets.UTF_8); DatagramPacket packet = new DatagramPacket(content, content.length); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportNegotiator.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportNegotiator.java index 127a7364c..c61ffd46f 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportNegotiator.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportNegotiator.java @@ -95,7 +95,7 @@ public abstract class TransportNegotiator extends JingleNegotiator { * * @param session The Jingle session * @param transResolver The JingleTransportManager to use - * @param parentNegotiator the parent ngeotiator. + * @param parentNegotiator the parent negotiator. */ public TransportNegotiator(JingleSession session, TransportResolver transResolver, ContentNegotiator parentNegotiator) { super(session); @@ -311,7 +311,7 @@ public abstract class TransportNegotiator extends JingleNegotiator { // Sleep for some time, waiting for the candidates checks int totalTime = CANDIDATES_ACCEPT_PERIOD + TransportResolver.CHECK_TIMEOUT; - int tries = (int) Math.ceil(totalTime / 1000); + int tries = (int) Math.ceil(totalTime / 1000.0); for (int i = 0; i < tries - 1; i++) { try { @@ -478,7 +478,7 @@ public abstract class TransportNegotiator extends JingleNegotiator { * * @return The list of valid (ie, already checked) remote candidates. */ - final ArrayList getValidRemoteCandidatesList() { + final List getValidRemoteCandidatesList() { synchronized (validRemoteCandidates) { return new ArrayList<>(validRemoteCandidates); } @@ -872,7 +872,7 @@ public abstract class TransportNegotiator extends JingleNegotiator { @Override public TransportCandidate getBestRemoteCandidate() { // Hopefully, we only have one validRemoteCandidate - ArrayList cands = getValidRemoteCandidatesList(); + List cands = getValidRemoteCandidatesList(); if (!cands.isEmpty()) { LOGGER.fine("RAW CAND"); return cands.get(0); @@ -930,7 +930,7 @@ public abstract class TransportNegotiator extends JingleNegotiator { public TransportCandidate getBestRemoteCandidate() { ICECandidate result = null; - ArrayList cands = getValidRemoteCandidatesList(); + List cands = getValidRemoteCandidatesList(); if (!cands.isEmpty()) { int highest = -1; ICECandidate chose = null; diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportResolver.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportResolver.java index 75c1e2f71..9393dd1c3 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportResolver.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportResolver.java @@ -210,7 +210,7 @@ public abstract class TransportResolver { * * @return the list of listeners */ - public ArrayList getListenersList() { + public List getListenersList() { synchronized (listeners) { return new ArrayList<>(listeners); } @@ -236,7 +236,7 @@ public abstract class TransportResolver { } /** - * Trigger a event notifying the initialization of the resolution process. + * Trigger an event notifying the initialization of the resolution process. */ private void triggerResolveInit() { Iterator iter = getListenersList().iterator(); @@ -250,7 +250,7 @@ public abstract class TransportResolver { } /** - * Trigger a event notifying the obtainment of all the candidates. + * Trigger an event notifying the obtainment of all the candidates. */ private void triggerResolveEnd() { Iterator iter = getListenersList().iterator(); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/Jingle.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/Jingle.java index d904f0d34..9ac5270f6 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/Jingle.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/Jingle.java @@ -71,6 +71,7 @@ public class Jingle extends IQ { * @param mi the jingle content info * @param sid the sid. */ + @SuppressWarnings("this-escape") public Jingle(final List contents, final JingleContentInfo mi, final String sid) { this(); @@ -93,6 +94,7 @@ public class Jingle extends IQ { * * @param content a content */ + @SuppressWarnings("this-escape") public Jingle(final JingleContent content) { this(); @@ -112,6 +114,7 @@ public class Jingle extends IQ { * * @param info The content info */ + @SuppressWarnings("this-escape") public Jingle(final JingleContentInfo info) { this(); @@ -131,6 +134,7 @@ public class Jingle extends IQ { * * @param action The action. */ + @SuppressWarnings("this-escape") public Jingle(final JingleActionEnum action) { this(null, null, null); this.action = action; diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleContentDescription.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleContentDescription.java index 3df98e574..53eee1d8a 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleContentDescription.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleContentDescription.java @@ -105,7 +105,7 @@ public abstract class JingleContentDescription implements ExtensionElement { * * @return a list for the audio payloads in the packet. */ - public ArrayList getJinglePayloadTypesList() { + public List getJinglePayloadTypesList() { synchronized (payloads) { return new ArrayList<>(payloads); } @@ -116,7 +116,7 @@ public abstract class JingleContentDescription implements ExtensionElement { * * @return a list of PayloadType.Audio */ - public ArrayList getAudioPayloadTypesList() { + public List getAudioPayloadTypesList() { ArrayList result = new ArrayList<>(); Iterator jinglePtsIter = getJinglePayloadTypes(); @@ -184,6 +184,7 @@ public abstract class JingleContentDescription implements ExtensionElement { * * @param pt the payload type. */ + @SuppressWarnings("this-escape") public Audio(final JinglePayloadType pt) { super(); addJinglePayloadType(pt); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleContentInfo.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleContentInfo.java index 9d5d7795b..caed15f9d 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleContentInfo.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleContentInfo.java @@ -99,6 +99,7 @@ public class JingleContentInfo implements ExtensionElement { public static final String NAMESPACE = "urn:xmpp:tmp:jingle:apps:rtp"; + @SuppressWarnings("this-escape") public Audio(final ContentInfo mi) { super(mi); setNamespace(NAMESPACE); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleDescription.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleDescription.java index 4e143f266..f2d2fdf82 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleDescription.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleDescription.java @@ -191,6 +191,7 @@ public abstract class JingleDescription implements ExtensionElement { * * @param pt the payload type. */ + @SuppressWarnings("this-escape") public Audio(final PayloadType pt) { super(); addPayloadType(pt); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleTransport.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleTransport.java index 40553ea27..1162df9d9 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleTransport.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/packet/JingleTransport.java @@ -55,6 +55,7 @@ public class JingleTransport implements ExtensionElement { * * @param candidate A transport candidate element to add. */ + @SuppressWarnings("this-escape") public JingleTransport(final JingleTransportCandidate candidate) { super(); addCandidate(candidate); @@ -202,6 +203,7 @@ public class JingleTransport implements ExtensionElement { * * @param candidate the jmf transport candidate */ + @SuppressWarnings("this-escape") public JingleTransportCandidate(final TransportCandidate candidate) { super(); setMediaTransport(candidate); @@ -272,6 +274,7 @@ public class JingleTransport implements ExtensionElement { public static class Ice extends JingleTransport { public static final String NAMESPACE = "urn:xmpp:tmp:jingle:transports:ice-udp"; + @SuppressWarnings("this-escape") public Ice() { super(); setNamespace(NAMESPACE); @@ -359,6 +362,7 @@ public class JingleTransport implements ExtensionElement { public static class RawUdp extends JingleTransport { public static final String NAMESPACE = "http://www.xmpp.org/extensions/xep-0177.html#ns"; + @SuppressWarnings("this-escape") public RawUdp() { super(); setNamespace(NAMESPACE); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/provider/JingleTransportProvider.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/provider/JingleTransportProvider.java index 0edb7d887..b81e4f201 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/provider/JingleTransportProvider.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/provider/JingleTransportProvider.java @@ -88,7 +88,7 @@ public abstract class JingleTransportProvider extends ExtensionElementProvider listeners = new ArrayList<>(); private final Map> presenceMap = new HashMap<>(); // The roster is marked as initialized when at least a single roster packet - // has been recieved and processed. + // has been received and processed. boolean rosterInitialized = false; /** @@ -181,7 +181,7 @@ public class AgentRoster { /** * Returns true if the specified XMPP address is an agent in the workgroup. * - * @param jid the XMPP address of the agent (eg "jsmith@example.com"). The + * @param jid the XMPP address of the agent (e.g."jsmith@example.com"). The * address can be in any valid format (e.g. "domain/resource", "user@domain" * or "user@domain/resource"). * @return true if the XMPP address is an agent in the workgroup. @@ -203,12 +203,12 @@ public class AgentRoster { /** * Returns the presence info for a particular agent, or null if the agent - * is unavailable (offline) or if no presence information is available.

    + * is unavailable (offline) or if no presence information is available. * * @param user a fully qualified xmpp JID. The address could be in any valid format (e.g. * "domain/resource", "user@domain" or "user@domain/resource"). * @return the agent's current presence, or null if the agent is unavailable - * or if no presence information is available.. + * or if no presence information is available. */ public Presence getPresence(Jid user) { Jid key = getPresenceMapKey(user); diff --git a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentSession.java b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentSession.java index 4c2e57c52..0cdf52f9d 100644 --- a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentSession.java +++ b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/AgentSession.java @@ -349,15 +349,9 @@ public class AgentSession { new StanzaTypeFilter(Presence.class), FromMatchesFilter.create(workgroupJID)), presence); presence = collector.nextResultOrThrow(); - - // We can safely update this iv since we didn't get any error - this.online = online; } // Otherwise the user is going offline... else { - // Update this iv now since we don't care at this point of any error - this.online = online; - presence = connection.getStanzaFactory().buildPresenceStanza() .ofType(Presence.Type.unavailable) .to(workgroupJID) @@ -710,10 +704,11 @@ public class AgentSession { } } + @SuppressWarnings("JavaUtilDate") private void fireOfferRequestEvent(OfferRequestProvider.OfferRequestPacket requestPacket) { Offer offer = new Offer(this.connection, this, requestPacket.getUserID(), requestPacket.getUserJID(), this.getWorkgroupJID(), - new Date(new Date().getTime() + (requestPacket.getTimeout() * 1000)), + new Date(new Date().getTime() + (requestPacket.getTimeout() * 1000L)), requestPacket.getSessionID(), requestPacket.getMetaData(), requestPacket.getContent()); synchronized (offerListeners) { @@ -723,6 +718,7 @@ public class AgentSession { } } + @SuppressWarnings("JavaUtilDate") private void fireOfferRevokeEvent(OfferRevokeProvider.OfferRevokePacket orp) { RevokedOffer revokedOffer = new RevokedOffer(orp.getUserJID(), orp.getUserID(), this.getWorkgroupJID(), orp.getSessionID(), orp.getReason(), new Date()); diff --git a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/Offer.java b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/Offer.java index ece31d9c6..3449cd69b 100644 --- a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/Offer.java +++ b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/Offer.java @@ -125,7 +125,7 @@ public class Offer { } /** - * The fully qualified name of the workgroup (eg support@example.com). + * The fully qualified name of the workgroup (e.g.support@example.com). * * @return the name of the workgroup. */ @@ -137,7 +137,7 @@ public class Offer { * The date when the offer will expire. The agent must {@link #accept()} * the offer before the expiration date or the offer will lapse and be * routed to another agent. Alternatively, the agent can {@link #reject()} - * the offer at any time if they don't wish to accept it.. + * the offer at any time if they don't wish to accept it. * * @return the date at which this offer expires. */ diff --git a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferListener.java b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferListener.java index 93de6bf8f..a33c6797b 100644 --- a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferListener.java +++ b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/agent/OfferListener.java @@ -37,7 +37,7 @@ public interface OfferListener { void offerReceived (Offer request); /** - * The implementing class instance will be notified via this when the AgentSessino has received + * The implementing class instance will be notified via this when the AgentSession has received * a revocation of a previously extended offer. * * @param revokedOffer the RevokedOffer instance embodying the details of the revoked offer diff --git a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java index 87b03daf4..3fc5f4a1e 100644 --- a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java +++ b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java @@ -54,6 +54,7 @@ public class AgentChatHistory extends IQ { private final List agentChatSessions = new ArrayList<>(); + @SuppressWarnings("JavaUtilDate") public AgentChatHistory(EntityBareJid agentJID, int maxSessions, Date startDate) { this(); this.agentJID = agentJID; @@ -116,6 +117,7 @@ public class AgentChatHistory extends IQ { return agentChatHistory; } + @SuppressWarnings("JavaUtilDate") private static AgentChatSession parseChatSetting(XmlPullParser parser) throws XmlPullParserException, IOException { boolean done = false; diff --git a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java index abe8771ca..8917ebe3e 100644 --- a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java +++ b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java @@ -53,6 +53,7 @@ public class DepartQueuePacket extends IQ { * @param workgroup the workgroup to depart. * @param user the user to make depart from the queue. */ + @SuppressWarnings("this-escape") public DepartQueuePacket(EntityBareJid workgroup, EntityJid user) { super("depart-queue", "http://jabber.org/protocol/workgroup"); this.user = user; diff --git a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java index 19d7a0e3b..f29eb7738 100644 --- a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java +++ b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java @@ -145,6 +145,7 @@ public final class QueueDetails implements ExtensionElement { */ public static class Provider extends ExtensionElementProvider { + @SuppressWarnings("JavaUtilDate") @Override public QueueDetails parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, diff --git a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java index 00a227c61..499d3fac5 100644 --- a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java +++ b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java @@ -22,6 +22,7 @@ public class ChatSetting { private String value; private int type; + @SuppressWarnings("this-escape") public ChatSetting(String key, String value, int type) { setKey(key); setValue(value); diff --git a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java index ddec1a662..8613deb96 100644 --- a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java +++ b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java @@ -66,6 +66,7 @@ public class ChatSettings extends IQ { settings = new ArrayList<>(); } + @SuppressWarnings("this-escape") public ChatSettings(String key) { this(); setKey(key); diff --git a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/user/Workgroup.java b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/user/Workgroup.java index 59ad7c4dc..a9f7b400c 100644 --- a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/user/Workgroup.java +++ b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/user/Workgroup.java @@ -91,7 +91,7 @@ public class Workgroup { /** * Creates a new workgroup instance using the specified workgroup JID - * (eg support@workgroup.example.com) and XMPP connection. The connection must have + * (e.g.support@workgroup.example.com) and XMPP connection. The connection must have * undergone a successful login before being used to construct an instance of * this class. * @@ -99,6 +99,7 @@ public class Workgroup { * @param connection an XMPP connection which must have already undergone a * successful login. */ + @SuppressWarnings("this-escape") public Workgroup(EntityBareJid workgroupJID, XMPPConnection connection) { // Login must have been done before passing in connection. if (!connection.isAuthenticated()) { @@ -136,8 +137,8 @@ public class Workgroup { } }); - /** - * Internal handling of an invitation.Recieving an invitation removes the user from the queue. + /* + * Internal handling of an invitation. Receiving an invitation removes the user from the queue. */ MultiUserChatManager.getInstanceFor(connection).addInvitationListener( new org.jivesoftware.smackx.muc.InvitationListener() { @@ -162,7 +163,7 @@ public class Workgroup { } /** - * Returns the name of this workgroup (eg support@example.com). + * Returns the name of this workgroup (e.g.support@example.com). * * @return the name of the workgroup. */ @@ -735,9 +736,9 @@ public class Workgroup { } /** - * Asks the workgroup for it's Properties. + * Asks the workgroup for its Properties. * - * @param jid the jid of the user who's information you would like the workgroup to retreive. + * @param jid the jid of the user whose information you would like the workgroup to retrieve. * @return the WorkgroupProperties for the specified workgroup. * @throws XMPPErrorException if there was an XMPP error returned. * @throws NoResponseException if there was no response from the remote entity. diff --git a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java index 29170b479..24370229f 100644 --- a/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java +++ b/smack-legacy/src/main/java/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java @@ -19,7 +19,7 @@ package org.jivesoftware.smackx.workgroup.util; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import java.util.Hashtable; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -45,6 +45,7 @@ public class MetaDataUtils { * @throws XmlPullParserException if an error occurs while parsing the XML. * @throws IOException if an error occurs while parsing the XML. */ + @SuppressWarnings("MixedMutabilityReturnType") public static Map> parseMetaData(XmlPullParser parser) throws XmlPullParserException, IOException { XmlPullParser.Event eventType = parser.getEventType(); @@ -52,7 +53,7 @@ public class MetaDataUtils { if ((eventType == XmlPullParser.Event.START_ELEMENT) && parser.getName().equals(MetaData.ELEMENT_NAME) && parser.getNamespace().equals(MetaData.NAMESPACE)) { - Map> metaData = new Hashtable<>(); + Map> metaData = new LinkedHashMap<>(); eventType = parser.next(); diff --git a/smack-legacy/src/main/java/org/jivesoftware/smackx/xroster/RosterExchangeManager.java b/smack-legacy/src/main/java/org/jivesoftware/smackx/xroster/RosterExchangeManager.java index baac6db8c..25461a3cd 100644 --- a/smack-legacy/src/main/java/org/jivesoftware/smackx/xroster/RosterExchangeManager.java +++ b/smack-legacy/src/main/java/org/jivesoftware/smackx/xroster/RosterExchangeManager.java @@ -42,7 +42,7 @@ import org.jxmpp.jid.Jid; /** * - * Manages Roster exchanges. A RosterExchangeManager provides a high level access to send + * Manages Roster exchanges. A RosterExchangeManager provides high level access to send * rosters, roster groups and roster entries to XMPP clients. It also provides an easy way * to hook up custom logic when entries are received from another XMPP client through * RosterExchangeListeners. @@ -106,7 +106,7 @@ public class RosterExchangeManager { * Removes a listener from roster exchanges. The listener will be fired anytime roster * entries are received from remote XMPP clients. * - * @param rosterExchangeListener a roster exchange listener.. + * @param rosterExchangeListener a roster exchange listener. */ public void removeRosterListener(RosterExchangeListener rosterExchangeListener) { rosterExchangeListeners.remove(rosterExchangeListener); diff --git a/smack-legacy/src/main/java/org/jivesoftware/smackx/xroster/packet/RosterExchange.java b/smack-legacy/src/main/java/org/jivesoftware/smackx/xroster/packet/RosterExchange.java index 291393660..6ee6771f1 100644 --- a/smack-legacy/src/main/java/org/jivesoftware/smackx/xroster/packet/RosterExchange.java +++ b/smack-legacy/src/main/java/org/jivesoftware/smackx/xroster/packet/RosterExchange.java @@ -70,6 +70,7 @@ public class RosterExchange implements ExtensionElement { * * @param roster the roster to send to other XMPP entity. */ + @SuppressWarnings("this-escape") public RosterExchange(Roster roster) { // Add all the roster entries to the new RosterExchange for (RosterEntry rosterEntry : roster.getEntries()) { diff --git a/smack-omemo-signal-integration-test/build.gradle b/smack-omemo-signal-integration-test/build.gradle index a499edc0c..58414a4d6 100644 --- a/smack-omemo-signal-integration-test/build.gradle +++ b/smack-omemo-signal-integration-test/build.gradle @@ -1,17 +1,16 @@ -apply plugin: 'application' +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.application-conventions' +} description = """\ Smack integration tests for OMEMO using libsignal.""" -mainClassName = 'org.igniterealtime.smack.inttest.smack_omemo_signal.SmackOmemoSignalIntegrationTestFramework' -applicationDefaultJvmArgs = ["-enableassertions"] +application { + mainClass = 'org.igniterealtime.smack.inttest.smack_omemo_signal.SmackOmemoSignalIntegrationTestFramework' +} dependencies { api project(':smack-integration-test') api project(':smack-omemo-signal') } - -run { - // Pass all system properties down to the "application" run - systemProperties System.getProperties() -} diff --git a/smack-omemo-signal/build.gradle b/smack-omemo-signal/build.gradle index f29374f10..aa0887f94 100644 --- a/smack-omemo-signal/build.gradle +++ b/smack-omemo-signal/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} + description=""" Smack API for XEP-0384: OMEMO Encryption using libsignal """ @@ -8,6 +13,9 @@ dependencies { api project(":smack-omemo") implementation 'org.whispersystems:signal-protocol-java:2.8.1' + // TODO: Migrate Junit4 tests to Junit5. + testImplementation "org.junit.vintage:junit-vintage-engine:$junitVersion" + testFixturesApi(testFixtures(project(":smack-core"))) testImplementation project(path: ":smack-omemo", configuration: "testRuntime") } diff --git a/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoKeyUtil.java b/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoKeyUtil.java index 010eb65ed..c4e5a78d7 100644 --- a/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoKeyUtil.java +++ b/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoKeyUtil.java @@ -55,6 +55,7 @@ public class SignalOmemoKeyUtil extends OmemoKeyUtil generateOmemoPreKeys(int currentPreKeyId, int count) { List preKeyRecords = KeyHelper.generatePreKeys(currentPreKeyId, count); TreeMap map = new TreeMap<>(); diff --git a/smack-omemo/build.gradle b/smack-omemo/build.gradle index bee2d806c..ca38595a0 100644 --- a/smack-omemo/build.gradle +++ b/smack-omemo/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} + description=""" Smack API for XEP-0384: OMEMO Encryption """ @@ -7,5 +12,8 @@ dependencies { api project(":smack-extensions") api project(":smack-experimental") + // TODO: Migrate Junit4 tests to Junit5. + testImplementation "org.junit.vintage:junit-vintage-engine:$junitVersion" + testFixturesApi(testFixtures(project(":smack-core"))) } diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/CachingOmemoStore.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/CachingOmemoStore.java index 766141b5c..f4c546bb3 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/CachingOmemoStore.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/CachingOmemoStore.java @@ -19,6 +19,7 @@ package org.jivesoftware.smackx.omemo; import java.io.IOException; import java.util.Date; import java.util.HashMap; +import java.util.Map; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; @@ -268,8 +269,9 @@ public class CachingOmemoStore loadOmemoPreKeys(OmemoDevice userDevice) throws IOException { - TreeMap preKeys = getCache(userDevice).preKeys; + Map preKeys = getCache(userDevice).preKeys; if (preKeys.isEmpty() && persistent != null) { preKeys.putAll(persistent.loadOmemoPreKeys(userDevice)); @@ -293,8 +295,9 @@ public class CachingOmemoStore loadOmemoSignedPreKeys(OmemoDevice userDevice) throws IOException { - TreeMap sigPreKeys = getCache(userDevice).signedPreKeys; + Map sigPreKeys = getCache(userDevice).signedPreKeys; if (sigPreKeys.isEmpty() && persistent != null) { sigPreKeys.putAll(persistent.loadOmemoSignedPreKeys(userDevice)); @@ -341,7 +344,7 @@ public class CachingOmemoStore loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) throws IOException { + public Map loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) throws IOException { HashMap sessions = getCache(userDevice).sessions.get(contact); if (sessions == null) { sessions = new HashMap<>(); diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/FileBasedOmemoStore.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/FileBasedOmemoStore.java index 62b742db5..b05ffddb9 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/FileBasedOmemoStore.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/FileBasedOmemoStore.java @@ -125,12 +125,14 @@ public abstract class FileBasedOmemoStore loadOmemoPreKeys(OmemoDevice userDevice) throws IOException { File preKeyDirectory = hierarchy.getPreKeysDirectory(userDevice); TreeMap preKeys = new TreeMap<>(); @@ -234,6 +241,7 @@ public abstract class FileBasedOmemoStore loadOmemoSignedPreKeys(OmemoDevice userDevice) throws IOException { File signedPreKeysDirectory = hierarchy.getSignedPreKeysDirectory(userDevice); TreeMap signedPreKeys = new TreeMap<>(); @@ -290,6 +298,7 @@ public abstract class FileBasedOmemoStore loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) throws IOException { File contactsDirectory = hierarchy.getContactsDir(userDevice, contact); HashMap sessions = new HashMap<>(); @@ -517,6 +526,7 @@ public abstract class FileBasedOmemoStore stack = new Stack<>(); diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java index 92889f14e..5745204ba 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java @@ -25,6 +25,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Random; import java.util.Set; import java.util.SortedSet; @@ -633,7 +634,7 @@ public final class OmemoManager extends Manager { * @throws SmackException.NoResponseException if there was no response from the remote entity. * @throws IOException if an I/O error occurred. */ - public synchronized HashMap getActiveFingerprints(BareJid contact) + public synchronized Map getActiveFingerprints(BareJid contact) throws SmackException.NotLoggedInException, CorruptedOmemoKeyException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, IOException { @@ -641,7 +642,7 @@ public final class OmemoManager extends Manager { throw new SmackException.NotLoggedInException(); } - HashMap fingerprints = new HashMap<>(); + Map fingerprints = new HashMap<>(); OmemoCachedDeviceList deviceList = getOmemoService().getOmemoStoreBackend().loadCachedDeviceList(getOwnDevice(), contact); diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoMessage.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoMessage.java index e2eb8635a..9f13b13c2 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoMessage.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoMessage.java @@ -22,6 +22,7 @@ import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_ import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; import org.jivesoftware.smack.packet.Message; @@ -78,7 +79,7 @@ public class OmemoMessage { */ public static class Sent extends OmemoMessage { private final Set intendedDevices = new HashSet<>(); - private final HashMap skippedDevices = new HashMap<>(); + private final Map skippedDevices = new HashMap<>(); /** * Create a new outgoing OMEMO message. @@ -90,7 +91,7 @@ public class OmemoMessage { * @param skippedDevices devices which were skipped during encryption process because encryption * failed for some reason */ - Sent(OmemoElement element, byte[] key, byte[] iv, Set intendedDevices, HashMap skippedDevices) { + Sent(OmemoElement element, byte[] key, byte[] iv, Set intendedDevices, Map skippedDevices) { super(element, key, iv); this.intendedDevices.addAll(intendedDevices); this.skippedDevices.putAll(skippedDevices); @@ -110,7 +111,7 @@ public class OmemoMessage { * * @return map of skipped recipients and reasons for that. */ - public HashMap getSkippedDevices() { + public Map getSkippedDevices() { return skippedDevices; } diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java index 82266bf2a..4573a44ab 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java @@ -30,6 +30,7 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Random; import java.util.Set; import java.util.logging.Level; @@ -794,7 +795,7 @@ public abstract class OmemoService bundlesList = getOmemoStoreBackend().keyUtil().BUNDLE.bundles(bundleElement, contactsDevice); + Map bundlesList = getOmemoStoreBackend().keyUtil().BUNDLE.bundles(bundleElement, contactsDevice); int randomIndex = new Random().nextInt(bundlesList.size()); T_Bundle randomPreKeyBundle = new ArrayList<>(bundlesList.values()).get(randomIndex); @@ -893,7 +894,7 @@ public abstract class OmemoService signedPreKeys = loadOmemoSignedPreKeys(userDevice); + Map signedPreKeys = loadOmemoSignedPreKeys(userDevice); if (signedPreKeys.size() == 0) { changeSignedPreKey(userDevice); } @@ -237,7 +238,7 @@ public abstract class OmemoStore 0) { - TreeMap newKeys = generateOmemoPreKeys(startId + 1, newKeysCount); + Map newKeys = generateOmemoPreKeys(startId + 1, newKeysCount); storeOmemoPreKeys(userDevice, newKeys); } } @@ -415,6 +416,7 @@ public abstract class OmemoStore generateOmemoPreKeys(int startId, int count) { return keyUtil().generateOmemoPreKeys(startId, count); } @@ -449,7 +451,7 @@ public abstract class OmemoStore preKeyHashMap) throws IOException { + public void storeOmemoPreKeys(OmemoDevice userDevice, Map preKeyHashMap) throws IOException { for (Map.Entry entry : preKeyHashMap.entrySet()) { storeOmemoPreKey(userDevice, entry.getKey(), entry.getValue()); } @@ -472,6 +474,7 @@ public abstract class OmemoStore loadOmemoPreKeys(OmemoDevice userDevice) throws IOException; /** @@ -497,6 +500,8 @@ public abstract class OmemoStore loadOmemoSignedPreKeys(OmemoDevice userDevice) throws IOException; /** @@ -552,7 +557,7 @@ public abstract class OmemoStore loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) throws IOException; + public abstract Map loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) throws IOException; /** * Store a crypto-lib specific session to storage. diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoBundleElement.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoBundleElement.java index 217c27f6d..610e07515 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoBundleElement.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoBundleElement.java @@ -47,8 +47,8 @@ public abstract class OmemoBundleElement implements ExtensionElement { private byte[] signedPreKeySignature; private final String identityKeyB64; private byte[] identityKey; - private final HashMap preKeysB64; - private HashMap preKeys; + private final Map preKeysB64; + private Map preKeys; /** * Constructor to create a Bundle Element from base64 Strings. @@ -57,9 +57,9 @@ public abstract class OmemoBundleElement implements ExtensionElement { * @param signedPreKeyB64 base64 encoded signedPreKey * @param signedPreKeySigB64 base64 encoded signedPreKeySignature * @param identityKeyB64 base64 encoded identityKey - * @param preKeysB64 HashMap of base64 encoded preKeys + * @param preKeysB64 Map of base64 encoded preKeys */ - public OmemoBundleElement(int signedPreKeyId, String signedPreKeyB64, String signedPreKeySigB64, String identityKeyB64, HashMap preKeysB64) { + public OmemoBundleElement(int signedPreKeyId, String signedPreKeyB64, String signedPreKeySigB64, String identityKeyB64, Map preKeysB64) { if (signedPreKeyId < 0) { throw new IllegalArgumentException("signedPreKeyId MUST be greater than or equal to 0."); } @@ -81,9 +81,9 @@ public abstract class OmemoBundleElement implements ExtensionElement { * @param signedPreKey signedPreKey * @param signedPreKeySig signedPreKeySignature * @param identityKey identityKey - * @param preKeys HashMap of preKeys + * @param preKeys Map of preKeys */ - public OmemoBundleElement(int signedPreKeyId, byte[] signedPreKey, byte[] signedPreKeySig, byte[] identityKey, HashMap preKeys) { + public OmemoBundleElement(int signedPreKeyId, byte[] signedPreKey, byte[] signedPreKeySig, byte[] identityKey, Map preKeys) { this(signedPreKeyId, signedPreKey != null ? Base64.encodeToString(signedPreKey) : null, signedPreKeySig != null ? Base64.encodeToString(signedPreKeySig) : null, @@ -95,12 +95,12 @@ public abstract class OmemoBundleElement implements ExtensionElement { this.preKeys = preKeys; } - private static HashMap base64EncodePreKeys(HashMap preKeys) { + private static Map base64EncodePreKeys(Map preKeys) { if (preKeys == null) { return null; } - HashMap converted = new HashMap<>(); + Map converted = new HashMap<>(); for (Integer id : preKeys.keySet()) { converted.put(id, Base64.encodeToString(preKeys.get(id))); } @@ -155,12 +155,12 @@ public abstract class OmemoBundleElement implements ExtensionElement { } /** - * Return the HashMap of preKeys in the bundle. + * Return the Map of preKeys in the bundle. * The map uses the preKeys ids as key and the preKeys as value. * * @return preKeys Pre-Keys contained in the bundle */ - public HashMap getPreKeys() { + public Map getPreKeys() { if (preKeys == null) { preKeys = new HashMap<>(); for (int id : preKeysB64.keySet()) { diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoBundleElement_VAxolotl.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoBundleElement_VAxolotl.java index bcf616111..e3ae2d8aa 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoBundleElement_VAxolotl.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoBundleElement_VAxolotl.java @@ -18,7 +18,7 @@ package org.jivesoftware.smackx.omemo.element; import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL; -import java.util.HashMap; +import java.util.Map; /** * OMEMO device bundle as described by the protocol. @@ -29,11 +29,11 @@ import java.util.HashMap; */ public class OmemoBundleElement_VAxolotl extends OmemoBundleElement { - public OmemoBundleElement_VAxolotl(int signedPreKeyId, String signedPreKeyB64, String signedPreKeySigB64, String identityKeyB64, HashMap preKeysB64) { + public OmemoBundleElement_VAxolotl(int signedPreKeyId, String signedPreKeyB64, String signedPreKeySigB64, String identityKeyB64, Map preKeysB64) { super(signedPreKeyId, signedPreKeyB64, signedPreKeySigB64, identityKeyB64, preKeysB64); } - public OmemoBundleElement_VAxolotl(int signedPreKeyId, byte[] signedPreKey, byte[] signedPreKeySig, byte[] identityKey, HashMap preKeys) { + public OmemoBundleElement_VAxolotl(int signedPreKeyId, byte[] signedPreKey, byte[] signedPreKeySig, byte[] identityKey, Map preKeys) { super(signedPreKeyId, signedPreKey, signedPreKeySig, identityKey, preKeys); } diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoHeaderElement.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoHeaderElement.java index 4270d8ec5..050adf033 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoHeaderElement.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoHeaderElement.java @@ -57,7 +57,7 @@ public abstract class OmemoHeaderElement implements XmlElement { return sid; } - public ArrayList getKeys() { + public List getKeys() { return new ArrayList<>(keys); } diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/exceptions/CannotEstablishOmemoSessionException.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/exceptions/CannotEstablishOmemoSessionException.java index 2d4e65513..161dbc38f 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/exceptions/CannotEstablishOmemoSessionException.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/exceptions/CannotEstablishOmemoSessionException.java @@ -18,6 +18,7 @@ package org.jivesoftware.smackx.omemo.exceptions; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.jivesoftware.smackx.omemo.internal.OmemoDevice; @@ -32,8 +33,8 @@ import org.jxmpp.jid.BareJid; public class CannotEstablishOmemoSessionException extends Exception { private static final long serialVersionUID = 3165844730283295249L; - private final HashMap> failures = new HashMap<>(); - private final HashMap> successes = new HashMap<>(); + private final Map> failures = new HashMap<>(); + private final Map> successes = new HashMap<>(); public CannotEstablishOmemoSessionException(OmemoDevice failed, Throwable reason) { super(); @@ -41,7 +42,7 @@ public class CannotEstablishOmemoSessionException extends Exception { } public void addFailures(CannotEstablishOmemoSessionException otherFailures) { - for (Map.Entry> entry : otherFailures.getFailures().entrySet()) { + for (Map.Entry> entry : otherFailures.getFailures().entrySet()) { getFailsOfContact(entry.getKey()).putAll(entry.getValue()); } } @@ -50,16 +51,16 @@ public class CannotEstablishOmemoSessionException extends Exception { getSuccessesOfContact(success.getJid()).add(success); } - public HashMap> getFailures() { + public Map> getFailures() { return failures; } - public HashMap> getSuccesses() { + public Map> getSuccesses() { return successes; } - private HashMap getFailsOfContact(BareJid contact) { - HashMap h = failures.get(contact); + private Map getFailsOfContact(BareJid contact) { + Map h = failures.get(contact); if (h == null) { h = new HashMap<>(); failures.put(contact, h); @@ -67,8 +68,8 @@ public class CannotEstablishOmemoSessionException extends Exception { return h; } - private ArrayList getSuccessesOfContact(BareJid contact) { - ArrayList suc = successes.get(contact); + private List getSuccessesOfContact(BareJid contact) { + List suc = successes.get(contact); if (suc == null) { suc = new ArrayList<>(); successes.put(contact, suc); @@ -83,8 +84,8 @@ public class CannotEstablishOmemoSessionException extends Exception { * @return true if the exception requires to be thrown */ public boolean requiresThrowing() { - for (Map.Entry> entry : failures.entrySet()) { - ArrayList suc = successes.get(entry.getKey()); + for (Map.Entry> entry : failures.entrySet()) { + List suc = successes.get(entry.getKey()); if (suc == null || suc.isEmpty()) { return true; } diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/package-info.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/package-info.java index b8e0016ba..d4e6fbddd 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/package-info.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/package-info.java @@ -143,7 +143,7 @@ * *

    * The `setup()` method registers the service as a singleton. You can later access the instance by calling - * `SignalOmemoService.getInstace()`. The service can only be registered once. Subsequent calls will throw an + * `SignalOmemoService.getInstance()`. The service can only be registered once. Subsequent calls will throw an * {@link IllegalStateException}. *

    *

    2. Set an OmemoStore

    diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoKeyUtil.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoKeyUtil.java index 607181552..39f9ace13 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoKeyUtil.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoKeyUtil.java @@ -119,8 +119,8 @@ public abstract class OmemoKeyUtil bundles(OmemoBundleElement bundle, OmemoDevice contact) throws CorruptedOmemoKeyException { - HashMap bundles = new HashMap<>(); + public Map bundles(OmemoBundleElement bundle, OmemoDevice contact) throws CorruptedOmemoKeyException { + Map bundles = new HashMap<>(); for (int deviceId : bundle.getPreKeys().keySet()) { try { bundles.put(deviceId, bundleFromOmemoBundle(bundle, contact, deviceId)); @@ -211,6 +211,8 @@ public abstract class OmemoKeyUtil generateOmemoPreKeys(int startId, int count); /** @@ -338,8 +340,8 @@ public abstract class OmemoKeyUtil preKeyPublicKeysForBundle(TreeMap preKeyHashMap) { - HashMap out = new HashMap<>(); + public Map preKeyPublicKeysForBundle(Map preKeyHashMap) { + Map out = new HashMap<>(); for (Map.Entry e : preKeyHashMap.entrySet()) { out.put(e.getKey(), preKeyForBundle(e.getValue())); } diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoMessageBuilder.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoMessageBuilder.java index 0810fe1d1..5144b4f02 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoMessageBuilder.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoMessageBuilder.java @@ -83,7 +83,7 @@ public class OmemoMessageBuilder bundles = keyUtil.BUNDLE.bundles(bundle, device); + Map bundles = keyUtil.BUNDLE.bundles(bundle, device); assertEquals("There must be 100 bundles in the HashMap.", 100, bundles.size()); assertNotNull(keyUtil.BUNDLE.identityKey(bundle)); diff --git a/smack-omemo/src/test/java/org/jivesoftware/smackx/omemo/OmemoServiceTest.java b/smack-omemo/src/test/java/org/jivesoftware/smackx/omemo/OmemoServiceTest.java index 67dd244b6..0f228b161 100644 --- a/smack-omemo/src/test/java/org/jivesoftware/smackx/omemo/OmemoServiceTest.java +++ b/smack-omemo/src/test/java/org/jivesoftware/smackx/omemo/OmemoServiceTest.java @@ -50,6 +50,7 @@ public class OmemoServiceTest extends SmackTestSuite { * @throws XmppStringprepException if the provided string is invalid. */ @Test + @SuppressWarnings("JavaUtilDate") public void isStaleDeviceTest() throws XmppStringprepException { OmemoDevice user = new OmemoDevice(JidCreate.bareFrom("alice@wonderland.lit"), 123); OmemoDevice other = new OmemoDevice(JidCreate.bareFrom("bob@builder.tv"), 444); diff --git a/smack-omemo/src/test/java/org/jivesoftware/smackx/omemo/OmemoStoreTest.java b/smack-omemo/src/test/java/org/jivesoftware/smackx/omemo/OmemoStoreTest.java index 909aad68c..22b626054 100644 --- a/smack-omemo/src/test/java/org/jivesoftware/smackx/omemo/OmemoStoreTest.java +++ b/smack-omemo/src/test/java/org/jivesoftware/smackx/omemo/OmemoStoreTest.java @@ -27,8 +27,8 @@ import static junit.framework.TestCase.assertTrue; import java.io.IOException; import java.util.Arrays; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.TreeMap; import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException; @@ -174,7 +174,7 @@ public abstract class OmemoStoreTest before = store.generateOmemoPreKeys(1, 10); + Map before = store.generateOmemoPreKeys(1, 10); assertEquals("The store must have no prekeys before this test.", 0, store.loadOmemoPreKeys(alice).size()); store.storeOmemoPreKeys(alice, before); @@ -247,8 +247,8 @@ public abstract class OmemoStoreTest sessions = store.loadAllRawSessionsOf(alice, bob.getJid()); + Map sessions = store.loadAllRawSessionsOf(alice, bob.getJid()); assertNotNull(sessions); assertEquals(0, sessions.size()); } diff --git a/smack-openpgp/build.gradle b/smack-openpgp/build.gradle index 65484bde3..1a1fcaafe 100644 --- a/smack-openpgp/build.gradle +++ b/smack-openpgp/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} + description = """\ Smack API for XEP-0373: OpenPGP for XMPP.""" @@ -14,4 +19,7 @@ dependencies { testFixturesApi(testFixtures(project(":smack-core"))) testImplementation group: 'commons-io', name: 'commons-io', version: "$commonsIoVersion" + + // TODO: Migrate Junit4 tests to Junit5. + testImplementation "org.junit.vintage:junit-vintage-engine:$junitVersion" } diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpContact.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpContact.java index b7ffa0e71..c575fc5b7 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpContact.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpContact.java @@ -362,6 +362,7 @@ public class OpenPgpContact { * @throws SmackException.NoResponseException in case the server doesn't respond. * @throws IOException IO is dangerous. */ + @SuppressWarnings("JavaUtilDate") public void updateKeys(XMPPConnection connection, PublicKeysListElement metadata) throws InterruptedException, SmackException.NotConnectedException, SmackException.NoResponseException, IOException { diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java index 645f5bb66..b4a22c7cc 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java @@ -246,6 +246,7 @@ public final class OpenPgpManager extends Manager { * @throws SmackException.NotLoggedInException if we are not logged in. * @throws PGPException if something goes wrong during key loading/generating */ + @SuppressWarnings("JavaUtilDate") public void announceSupportAndPublish() throws NoSuchAlgorithmException, NoSuchProviderException, InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpSelf.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpSelf.java index 6ada87150..c9f6d6d17 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpSelf.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpSelf.java @@ -71,6 +71,7 @@ public class OpenPgpSelf extends OpenPgpContact { * @throws IOException IO is dangerous * @throws PGPException PGP is brittle */ + @SuppressWarnings("JavaUtilDate") public PGPSecretKeyRing getSigningKeyRing() throws IOException, PGPException { PGPSecretKeyRingCollection secretKeyRings = getSecretKeys(); if (secretKeyRings == null) { diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/EncryptedOpenPgpContentElement.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/EncryptedOpenPgpContentElement.java index a79dc8997..ecf49010f 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/EncryptedOpenPgpContentElement.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/EncryptedOpenPgpContentElement.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017-2019 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2017-2024 Florian Schmaus, 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,6 +45,7 @@ public abstract class EncryptedOpenPgpContentElement extends OpenPgpContentEleme this.rpad = Objects.requireNonNull(rpad); } + @SuppressWarnings("JavaUtilDate") protected EncryptedOpenPgpContentElement(Set to, List payload) { super(Objects.requireNonNullNorEmpty( to, "Encrypted OpenPGP content elements must have at least one 'to' attribute."), diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/OpenPgpContentElement.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/OpenPgpContentElement.java index 974185573..07d9327cb 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/OpenPgpContentElement.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/OpenPgpContentElement.java @@ -132,7 +132,7 @@ public abstract class OpenPgpContentElement implements ExtensionElement { * @param type of the ExtensionElement. * @return the extension, or null if it doesn't exist. */ - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) public PE getExtension(String elementName, String namespace) { if (namespace == null) { return null; diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/PublicKeysListElement.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/PublicKeysListElement.java index d4b9e81c7..3f97d83e8 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/PublicKeysListElement.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/PublicKeysListElement.java @@ -40,7 +40,7 @@ public final class PublicKeysListElement implements ExtensionElement { private final Map metadata; - private PublicKeysListElement(TreeMap metadata) { + private PublicKeysListElement(Map metadata) { this.metadata = Collections.unmodifiableMap(Objects.requireNonNull(metadata)); } @@ -48,7 +48,8 @@ public final class PublicKeysListElement implements ExtensionElement { return new Builder(); } - public TreeMap getMetadata() { + @SuppressWarnings("NonApiType") + public Map getMetadata() { return new TreeMap<>(metadata); } @@ -72,7 +73,7 @@ public final class PublicKeysListElement implements ExtensionElement { public static final class Builder { - private final TreeMap metadata = new TreeMap<>(); + private final Map metadata = new TreeMap<>(); private Builder() { // Empty @@ -133,12 +134,13 @@ public final class PublicKeysListElement implements ExtensionElement { return xml; } + @SuppressWarnings("JavaUtilDate") @Override public int hashCode() { return getV4Fingerprint().hashCode() + 3 * getDate().hashCode(); } - @SuppressWarnings("UndefinedEquals") + @SuppressWarnings({"UndefinedEquals", "JavaUtilDate"}) // TODO: Fix the UndefinedEquals due using Date.equals(Date) @Override public boolean equals(Object o) { diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/provider/OpenPgpContentElementProvider.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/provider/OpenPgpContentElementProvider.java index 9bba1ef5e..616ac8779 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/provider/OpenPgpContentElementProvider.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/provider/OpenPgpContentElementProvider.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017-2021 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2017-2024 Florian Schmaus, 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,9 +18,9 @@ package org.jivesoftware.smackx.ox.provider; import java.io.IOException; import java.text.ParseException; +import java.util.ArrayList; import java.util.Date; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.logging.Level; @@ -87,7 +87,7 @@ public abstract class OpenPgpContentElementProvider to = new HashSet<>(); Date timestamp = null; String rpad = null; - List payload = new LinkedList<>(); + List payload = new ArrayList<>(); outerloop: while (true) { XmlPullParser.Event tag = parser.next(); diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpMetadataStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpMetadataStore.java index 9d20e2cfa..d1f0b2d6b 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpMetadataStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/store/filebased/FileBasedOpenPgpMetadataStore.java @@ -118,6 +118,7 @@ public class FileBasedOpenPgpMetadataStore extends AbstractOpenPgpMetadataStore } } + @SuppressWarnings("JavaUtilDate") static void writeFingerprintsAndDates(Map data, File destination) throws IOException { if (data == null || data.isEmpty()) { diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpPubSubUtil.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpPubSubUtil.java index 0e418d80b..1ba31495d 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpPubSubUtil.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpPubSubUtil.java @@ -124,6 +124,7 @@ public class OpenPgpPubSubUtil { * @throws SmackException.NotConnectedException if we are not connected. * @throws SmackException.NoResponseException if the server doesn't respond. */ + @SuppressWarnings("JavaUtilDate") public static void publishPublicKey(PepManager pepManager, PubkeyElement pubkeyElement, OpenPgpV4Fingerprint fingerprint) throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException { @@ -408,7 +409,7 @@ public class OpenPgpPubSubUtil { * which are not subscribed to the node owner. Therefore this method fetches the node directly and puts it * into the {@link PubSubManager}s node map. * - * Note: Due to the alck of a disco#info query, it might happen, that the node doesn't exist on the server, + * Note: Due to the lack of a disco#info query, it might happen, that the node doesn't exist on the server, * even though we add it to the node map. * * @see Ejabberd bug tracker about the issue @@ -464,8 +465,7 @@ public class OpenPgpPubSubUtil { } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException | NoSuchFieldException e) { - LOGGER.log(Level.SEVERE, "Using reflections to create a LeafNode and put it into PubSubManagers nodeMap failed.", e); - throw new AssertionError(e); + throw new LinkageError("Using reflections to create a LeafNode and put it into PubSubManagers nodeMap failed.", e); } } } diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpElementTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpElementTest.java index 2c54a0d55..a958d1adb 100644 --- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpElementTest.java +++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpElementTest.java @@ -51,6 +51,7 @@ public class OpenPgpElementTest extends SmackTestSuite { private final Set recipients; // 2014-07-10T15:06:00.000+00:00 + @SuppressWarnings("JavaUtilDate") private static final Date testDate = new Date(1405004760000L); public OpenPgpElementTest() throws XmppStringprepException { diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpStoreTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpStoreTest.java index d64c1522c..30e3f05af 100644 --- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpStoreTest.java +++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpStoreTest.java @@ -300,6 +300,7 @@ public class OpenPgpStoreTest extends SmackTestSuite { */ @Test + @SuppressWarnings("JavaUtilDate") public void t10_meta_emptyStoreTest() throws IOException { assertNotNull(openPgpStoreInstance1.getAnnouncedFingerprintsOf(alice)); assertTrue(openPgpStoreInstance1.getAnnouncedFingerprintsOf(alice).isEmpty()); @@ -324,6 +325,7 @@ public class OpenPgpStoreTest extends SmackTestSuite { } @Test + @SuppressWarnings("JavaUtilDate") public void t11_key_fetchDateTest() throws IOException { Map fetchDates1 = openPgpStoreInstance1.getPublicKeyFetchDates(alice); diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PainlessOpenPgpProviderTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PainlessOpenPgpProviderTest.java index 5f1210367..41319bd6f 100644 --- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PainlessOpenPgpProviderTest.java +++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PainlessOpenPgpProviderTest.java @@ -72,6 +72,7 @@ public class PainlessOpenPgpProviderTest extends SmackTestSuite { } @Test + @SuppressWarnings("JavaUtilDate") public void encryptDecryptTest() throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, IOException, MissingUserIdOnKeyException, XmlPullParserException { // Initialize diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PublicKeysListElementTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PublicKeysListElementTest.java index 319c60bcf..eb7f3922c 100644 --- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PublicKeysListElementTest.java +++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PublicKeysListElementTest.java @@ -70,6 +70,7 @@ public class PublicKeysListElementTest extends SmackTestSuite { } @Test + @SuppressWarnings("JavaUtilDate") public void listBuilderRefusesDuplicatesTest() { PublicKeysListElement.Builder builder = PublicKeysListElement.builder(); String fp40 = "49545320414c4c2041424f555420444120484558"; diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManagerTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManagerTest.java index 7854aedce..668a14204 100644 --- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManagerTest.java +++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManagerTest.java @@ -66,6 +66,7 @@ public class OXInstantMessagingManagerTest extends SmackTestSuite { } @Test + @SuppressWarnings("JavaUtilDate") public void test() throws IOException, PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException, SmackException, MissingUserIdOnKeyException, InterruptedException, XMPPException, XmlPullParserException { diff --git a/smack-repl/build.gradle b/smack-repl/build.gradle index 6e098eb52..9b60cd9fb 100644 --- a/smack-repl/build.gradle +++ b/smack-repl/build.gradle @@ -1,31 +1,32 @@ plugins { - id "com.github.alisiikh.scalastyle_2.12" version "2.1.0" + id 'org.igniterealtime.smack.java-common-conventions' + id "com.github.alisiikh.scalastyle" version "3.5.0" } description = """\ A REPL (Read-Eval-Print-Loop) for Smack, or, in other words, a CLI (Command Line Interface) for Smack.""" apply plugin: 'scala' -apply plugin: 'com.github.alisiikh.scalastyle_2.12' +apply plugin: 'com.github.alisiikh.scalastyle' ext { - scalaVersion = '2.13.12' + scalaVersion = '2.13.13' } dependencies { api project(':smack-examples') implementation "org.scala-lang:scala-library:$scalaVersion" - implementation "com.lihaoyi:ammonite_$scalaVersion:2.5.11" + implementation "com.lihaoyi:ammonite_$scalaVersion:3.0.0-M1" } -scalaStyle { +scalastyle { config = new File(rootConfigDir, 'scalaStyle.xml') verbose = true - failOnViolation = true + failOnWarning = true } -check.dependsOn(scalaStyleCheck) +check.dependsOn(scalastyleCheck) task printClasspath(dependsOn: assemble) { doLast { diff --git a/smack-repl/scala.repl b/smack-repl/scala.repl index 1da355d19..0e0dee4db 100644 --- a/smack-repl/scala.repl +++ b/smack-repl/scala.repl @@ -5,4 +5,5 @@ import org.jivesoftware.smack.util.TLSUtils import org.jivesoftware.smack.tcp._ import org.jxmpp.jid.impl.JidCreate -import org.igniterealtime.smack.smackrepl.IoT._ +import org.igniterealtime.smack.examples._ +import org.igniterealtime.smack.examples.IoT._ diff --git a/smack-resolver-dnsjava/build.gradle b/smack-resolver-dnsjava/build.gradle index b17683ecc..b4e6b85cf 100644 --- a/smack-resolver-dnsjava/build.gradle +++ b/smack-resolver-dnsjava/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} + description = """\ DNS SRV with dnsjava Use dnsjava for DNS SRV lookups.""" diff --git a/smack-resolver-javax/build.gradle b/smack-resolver-javax/build.gradle index 4caefb6f5..f724be154 100644 --- a/smack-resolver-javax/build.gradle +++ b/smack-resolver-javax/build.gradle @@ -1,3 +1,7 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' +} + description = """\ DNS SRV with Java7 Use javax.naming for DNS SRV lookups. The javax.naming API is availabe in JavaSE diff --git a/smack-resolver-javax/src/main/java/org/jivesoftware/smack/util/dns/javax/JavaxResolver.java b/smack-resolver-javax/src/main/java/org/jivesoftware/smack/util/dns/javax/JavaxResolver.java index ea508f0fe..1ca3b5721 100644 --- a/smack-resolver-javax/src/main/java/org/jivesoftware/smack/util/dns/javax/JavaxResolver.java +++ b/smack-resolver-javax/src/main/java/org/jivesoftware/smack/util/dns/javax/JavaxResolver.java @@ -1,6 +1,6 @@ /** * - * Copyright 2013-2020 Florian Schmaus + * Copyright 2013-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,11 +40,12 @@ import org.minidns.record.SRV; /** * A DNS resolver (mostly for SRV records), which makes use of the API provided in the javax.* namespace. - * Note that using JavaxResovler requires applications using newer Java versions (at least 11) to declare a dependency on the "sun.jdk" module. + * Note that using JavaxResolver requires applications using newer Java versions (at least 11) to declare a dependency on the "sun.jdk" module. * * @author Florian Schmaus * */ +@SuppressWarnings("JdkObsolete") public class JavaxResolver extends DNSResolver implements SmackInitializer { private static JavaxResolver instance; @@ -83,6 +84,7 @@ public class JavaxResolver extends DNSResolver implements SmackInitializer { } @Override + @SuppressWarnings("BanJNDI") protected List lookupSrvRecords0(DnsName name, List lookupFailures, DnssecMode dnssecMode) { Attribute srvAttribute; diff --git a/smack-resolver-minidns-dox/build.gradle b/smack-resolver-minidns-dox/build.gradle index de740cf84..f8ebf7d3b 100644 --- a/smack-resolver-minidns-dox/build.gradle +++ b/smack-resolver-minidns-dox/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} + description = """\ DNS over XMPP (DoX) support using MiniDNS.""" diff --git a/smack-resolver-minidns/build.gradle b/smack-resolver-minidns/build.gradle index 22809986c..c1fb68a28 100644 --- a/smack-resolver-minidns/build.gradle +++ b/smack-resolver-minidns/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} + description = """\ DNS SRV with minidns Use minidns for DNS SRV lookups. For platforms that don't provide the diff --git a/smack-sasl-javax/build.gradle b/smack-sasl-javax/build.gradle index e6fffd631..9f2eecc89 100644 --- a/smack-sasl-javax/build.gradle +++ b/smack-sasl-javax/build.gradle @@ -1,3 +1,7 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' +} + description = """\ SASL with javax.security.sasl Use javax.security.sasl for SASL.""" diff --git a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLExternalMechanism.java b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLExternalMechanism.java index f0c409c49..fdc7822a8 100644 --- a/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLExternalMechanism.java +++ b/smack-sasl-javax/src/main/java/org/jivesoftware/smack/sasl/javax/SASLExternalMechanism.java @@ -24,7 +24,7 @@ package org.jivesoftware.smack.sasl.javax; * to the implementer to determine how to do this. Here is one method: * * Create a java keystore with your SSL certificate in it: - * keytool -genkey -alias username -dname "cn=username,ou=organizationalUnit,o=organizationaName,l=locality,s=state,c=country" + * keytool -genkey -alias username -dname "cn=username,ou=organizationalUnit,o=organizationalName,l=locality,s=state,c=country" * * Next, set the System Properties: *
      @@ -38,7 +38,7 @@ package org.jivesoftware.smack.sasl.javax; * simply provide the one in the keyStore. * * Also worth noting is the EXTERNAL mechanism in Smack is not enabled by default. - * To enable it, the implementer will need to call SASLAuthentication.supportSASLMechamism("EXTERNAL"); + * To enable it, the implementer will need to call SASLAuthentication.supportSASLMechanism("EXTERNAL"); * * @author Jay Kline */ diff --git a/smack-sasl-provided/build.gradle b/smack-sasl-provided/build.gradle index a9f3724d0..65f10b3b1 100644 --- a/smack-sasl-provided/build.gradle +++ b/smack-sasl-provided/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} + description = """\ SASL with Smack provided code Use Smack provided code for SASL.""" diff --git a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java index 2b1a89d0a..284ef767b 100644 --- a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java +++ b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java @@ -30,7 +30,7 @@ public class SASLDigestMD5Mechanism extends SASLMechanism { public static final String NAME = DIGESTMD5; - private static final String INITAL_NONCE = "00000001"; + private static final String INITIAL_NONCE = "00000001"; /** * The only 'qop' value supported by this implementation @@ -159,7 +159,7 @@ public class SASLDigestMD5Mechanism extends SASLMechanism { + ",realm=\"" + serviceName + '"' + ",nonce=\"" + nonce + '"' + ",cnonce=\"" + cnonce + '"' - + ",nc=" + INITAL_NONCE + + ",nc=" + INITIAL_NONCE + ",qop=auth" + ",digest-uri=\"" + digestUri + '"' + ",response=" + responseValue @@ -218,7 +218,7 @@ public class SASLDigestMD5Mechanism extends SASLMechanism { kd_argument.append(':'); kd_argument.append(nonce); kd_argument.append(':'); - kd_argument.append(INITAL_NONCE); + kd_argument.append(INITIAL_NONCE); kd_argument.append(':'); kd_argument.append(cnonce); kd_argument.append(':'); diff --git a/smack-streammanagement/build.gradle b/smack-streammanagement/build.gradle index c06c423d9..73eb2139e 100644 --- a/smack-streammanagement/build.gradle +++ b/smack-streammanagement/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} + description = """\ Smack support for XMPP Stream Management (XEP-0198).""" diff --git a/smack-tcp/build.gradle b/smack-tcp/build.gradle index a0cffad6b..db2ee6bc2 100644 --- a/smack-tcp/build.gradle +++ b/smack-tcp/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} + description = """\ Smack for standard XMPP connections over TCP.""" diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/sm/predicates/tcp/package-info.java b/smack-tcp/src/main/java/org/jivesoftware/smack/sm/predicates/tcp/package-info.java index 15e022848..d6f94b910 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/sm/predicates/tcp/package-info.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/sm/predicates/tcp/package-info.java @@ -16,6 +16,6 @@ */ /** - * XMPPTCPConnection Stream Managment Predicates. + * XMPPTCPConnection Stream Management Predicates. */ package org.jivesoftware.smack.sm.predicates.tcp; diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index 38d2d3e55..1e622bedc 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -31,7 +31,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -187,7 +186,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { private static boolean useSmResumptionDefault = true; /** - * The stream ID of the stream that is currently resumable, ie. the stream we hold the state + * The stream ID of the stream that is currently resumable, i.e. the stream we hold the state * for in {@link #clientHandledStanzasCount}, {@link #serverHandledStanzasCount} and * {@link #unacknowledgedStanzas}. */ @@ -203,7 +202,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { private Failed smResumptionFailed; /** - * Represents the state of stream magement. + * Represents the state of stream management. *

      * This boolean is marked volatile as it is read by various threads, including the reader thread via {@link #isSmEnabled()}. *

      @@ -295,6 +294,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { * * @param config the connection configuration. */ + @SuppressWarnings("this-escape") public XMPPTCPConnection(XMPPTCPConnectionConfiguration config) { super(config); this.config = config; @@ -415,7 +415,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // bind IQ may trigger a SM ack request, which would be invalid in the pre resource bound state. smEnabledSyncPoint = false; - List previouslyUnackedStanzas = new LinkedList(); + List previouslyUnackedStanzas = new ArrayList(); if (unacknowledgedStanzas != null) { // There was a previous connection with SM enabled but that was either not resumable or // failed to resume. Make sure that we (re-)send the unacknowledged stanzas. @@ -1162,8 +1162,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { } catch (Exception e) { // Set running to false since this thread will exit here and notifyConnectionError() will wait until - // the reader and writer thread's 'running' value is false. Hence we need to set it to false before calling - // notifyConnetctionError() below, even though run() also sets it to false. Therefore, do not remove this. + // the reader and writer thread's 'running' value is false. Hence, we need to set it to false before calling + // notifyConnectionError() below, even though run() also sets it to false. Therefore, do not remove this. running = false; String ignoreReasonThread = null; @@ -1256,8 +1256,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { final boolean smResumptionPossible = isSmResumptionPossible(); // Don't throw a NotConnectedException is there is an resumable stream available if (!smResumptionPossible) { - throw new NotConnectedException(XMPPTCPConnection.this, "done=" + done - + " smResumptionPossible=" + smResumptionPossible); + throw new NotConnectedException(XMPPTCPConnection.this, "done=true smResumptionPossible=false"); } } } @@ -1645,8 +1644,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { private void sendSmAcknowledgementInternal() throws NotConnectedException, InterruptedException { AckAnswer ackAnswer = new AckAnswer(clientHandledStanzasCount); // Do net put an ack to the queue if it has already been shutdown. Some servers, like ejabberd, like to request - // an ack even after we have send a stream close (and hance the queue was shutdown). If we would not check here, - // then the ack would dangle around in the queue, and be send on the next re-connection attempt even before the + // an ack even after we have sent a stream close (and hence the queue was shutdown). If we would not check here, + // then the ack would dangle around in the queue, and be sent on the next re-connection attempt even before the // stream open. packetWriter.queue.putIfNotShutdown(ackAnswer); } diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java index bd194fcda..fabfa4d6f 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019-2021 Florian Schmaus + * Copyright 2019-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -369,11 +369,11 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM newPendingOutputFilterData |= outputResult.pendingFilterData; outputFilterInputData = outputResult.filteredOutputData; if (outputFilterInputData != null) { - outputFilterInputData.flip(); + ((java.nio.Buffer) outputFilterInputData).flip(); } } - // It is ok if outpuFilterInputData is 'null' here, this is expected behavior. + // It is ok if outputFilterInputData is 'null' here, this is expected behavior. if (outputFilterInputData != null && outputFilterInputData.hasRemaining()) { filteredOutgoingBuffer = outputFilterInputData; } else { @@ -459,7 +459,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM } int bytesRead; - incomingBuffer.clear(); + ((java.nio.Buffer) incomingBuffer).clear(); try { bytesRead = selectedSocketChannel.read(incomingBuffer); } catch (IOException e) { @@ -474,7 +474,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM // read() may return -1 if the input side of a socket is shut down. // Note that we do not call notifyConnectionError() here because the connection may be // cleanly shutdown which would also cause read() to return '-1. I assume that this socket - // will be selected again, on which read() would throw an IOException, which will be catched + // will be selected again, on which read() would throw an IOException, which will be caught // and invoke notifyConnectionError() (see a few lines above). /* IOException exception = new IOException("NIO read() returned " + bytesRead); @@ -503,7 +503,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM ByteBuffer filteredIncomingBuffer = incomingBuffer; for (ListIterator it = connectionInternal.getXmppInputOutputFilterEndIterator(); it.hasPrevious();) { - filteredIncomingBuffer.flip(); + ((java.nio.Buffer) filteredIncomingBuffer).flip(); ByteBuffer newFilteredIncomingBuffer; try { @@ -518,7 +518,8 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM filteredIncomingBuffer = newFilteredIncomingBuffer; } - final int bytesReadAfterFilter = filteredIncomingBuffer.flip().remaining(); + ((java.nio.Buffer) filteredIncomingBuffer).flip(); + final int bytesReadAfterFilter = filteredIncomingBuffer.remaining(); totalBytesReadAfterFilter += bytesReadAfterFilter; @@ -633,7 +634,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM @Override protected void loadConnectionEndpoints(LookupConnectionEndpointsSuccess lookupConnectionEndpointsSuccess) { // The API contract stats that we will be given the instance we handed out with lookupConnectionEndpoints, - // which must be of type DiscoveredTcpEndpoints here. Hence if we can not cast it, then there is an internal + // which must be of type DiscoveredTcpEndpoints here. Hence, if we can not cast it, then there is an internal // Smack error. discoveredTcpEndpoints = (DiscoveredTcpEndpoints) lookupConnectionEndpointsSuccess; } @@ -711,7 +712,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM // Add OP_WRITE to the interested Ops, since we have now new things to write. Note that this may cause // multiple reactor threads to race to the channel selected callback in case we perform this right after - // a select() returned with this selection key in the selected-key set. Hence we use tryLock() in the + // a select() returned with this selection key in the selected-key set. Hence, we use tryLock() in the // channel selected callback to keep the invariant that only exactly one thread is performing the // callback. // Note that we need to perform setInterestedOps() *without* holding the channelSelectedCallbackLock, as @@ -776,7 +777,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM // TODO: It appears this should be done in a generic way. I'd assume we always // have to wait for stream features after the connection was established. If this is true then consider // moving this into State.AbstractTransport. But I am not yet 100% positive that this is the case for every - // transport. Hence keep it here for now. + // transport. Hence, keep it here for now. connectionInternal.newStreamOpenWaitForFeaturesSequence("stream features after initial connection"); return new TcpSocketConnectedResult(remoteAddress); @@ -980,7 +981,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM ByteBuffer[] outputDataArray = pendingOutputData.toArray(new ByteBuffer[pendingOutputData.size()]); - myNetData.clear(); + ((java.nio.Buffer) myNetData).clear(); while (true) { SSLEngineResult result; @@ -1037,7 +1038,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM newCapacity = 2 * myNetData.capacity(); } ByteBuffer newMyNetData = ByteBuffer.allocateDirect(newCapacity); - myNetData.flip(); + ((java.nio.Buffer) myNetData).flip(); newMyNetData.put(myNetData); myNetData = newMyNetData; continue; @@ -1060,12 +1061,12 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM int accumulatedDataBytes = pendingInputData.remaining() + inputData.remaining(); accumulatedData = ByteBuffer.allocate(accumulatedDataBytes); accumulatedData.put(pendingInputData) - .put(inputData) - .flip(); + .put(inputData); + ((java.nio.Buffer) accumulatedData).flip(); pendingInputData = null; } - peerAppData.clear(); + ((java.nio.Buffer) peerAppData).clear(); while (true) { SSLEngineResult result; @@ -1090,7 +1091,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM // A delegated task is asynchronously running. Take care of the remaining accumulatedData. addAsPendingInputData(accumulatedData); // Return here, as the async task created by handleHandshakeStatus will continue calling the - // cannelSelectedCallback. + // channelSelectedCallback. return null; case NEED_UNWRAP: continue; @@ -1114,7 +1115,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM switch (engineResultStatus) { case OK: // SSLEngine's unwrap() may not consume all bytes from the source buffer. If this is the case, then - // simply perform another unwrap until accumlatedData has no remaining bytes. + // simply perform another unwrap until accumulatedData has no remaining bytes. if (accumulatedData.hasRemaining()) { continue; } @@ -1143,7 +1144,8 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM // higher layer. That is, here 'byteBuffer' is typically 'incomingBuffer', which is a direct buffer only // allocated once per connection for performance reasons and hence re-used for read() calls. pendingInputData = ByteBuffer.allocate(byteBuffer.remaining()); - pendingInputData.put(byteBuffer).flip(); + pendingInputData.put(byteBuffer); + ((java.nio.Buffer) pendingInputData).flip(); pendingInputFilterData = pendingInputData.hasRemaining(); } diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModuleDescriptor.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModuleDescriptor.java index c48d8c546..0dc5d40a4 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModuleDescriptor.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModuleDescriptor.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019-2020 Florian Schmaus + * Copyright 2019-2024 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -65,6 +65,8 @@ public class XmppTcpTransportModuleDescriptor extends ModularXmppClientToServerC public static final class Builder extends ModularXmppClientToServerConnectionModuleDescriptor.Builder { + // Invoked via reflection. + @SuppressWarnings("UnusedMethod") private Builder(ModularXmppClientToServerConnectionConfiguration.Builder connectionConfigurationBuilder) { super(connectionConfigurationBuilder); } diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/rce/RemoteXmppTcpConnectionEndpoints.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/rce/RemoteXmppTcpConnectionEndpoints.java index 2b2514dbb..d46344d93 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/rce/RemoteXmppTcpConnectionEndpoints.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/rce/RemoteXmppTcpConnectionEndpoints.java @@ -169,7 +169,7 @@ public class RemoteXmppTcpConnectionEndpoints { * * @param domain the domain. * @param domainType the XMPP domain type, server or client. - * @param lookupFailures a list that will be populated with all failures that oocured during lookup. + * @param lookupFailures a list that will be populated with all failures that occurred during lookup. * @param dnssecMode the DNSSEC mode. * @param dnsResolver the DNS resolver to use. * @return a list of resolved host addresses for this domain. diff --git a/smack-websocket-java11/build.gradle b/smack-websocket-java11/build.gradle index 9439bad20..c019f214c 100644 --- a/smack-websocket-java11/build.gradle +++ b/smack-websocket-java11/build.gradle @@ -1,3 +1,7 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' +} + description = """\ Smack for XMPP connections over WebSocket (RFC 7395) using java.net.http.WebSocket.""" diff --git a/smack-websocket-okhttp/build.gradle b/smack-websocket-okhttp/build.gradle index 6f83f2c83..9e7b2fb46 100644 --- a/smack-websocket-okhttp/build.gradle +++ b/smack-websocket-okhttp/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} + description = """\ Smack for XMPP connections over WebSocket (RFC 7395) using OkHttp.""" diff --git a/smack-websocket/build.gradle b/smack-websocket/build.gradle index 1ef60c233..73dfab23b 100644 --- a/smack-websocket/build.gradle +++ b/smack-websocket/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} + description = """\ Smack for XMPP connections over WebSocket (RFC 7395).""" diff --git a/smack-websocket/src/main/java/org/jivesoftware/smack/websocket/WebSocketConnectionAttemptState.java b/smack-websocket/src/main/java/org/jivesoftware/smack/websocket/WebSocketConnectionAttemptState.java index 80f58ab42..222aafc65 100644 --- a/smack-websocket/src/main/java/org/jivesoftware/smack/websocket/WebSocketConnectionAttemptState.java +++ b/smack-websocket/src/main/java/org/jivesoftware/smack/websocket/WebSocketConnectionAttemptState.java @@ -51,7 +51,7 @@ public final class WebSocketConnectionAttemptState { /** * Establish a websocket connection with one of the discoveredRemoteConnectionEndpoints.
      * - * @return {@link AbstractWebSocket} with which connection is establised + * @return {@link AbstractWebSocket} with which connection is established * @throws InterruptedException if the calling thread was interrupted */ @SuppressWarnings({"incomplete-switch", "MissingCasesInEnumSwitch"}) diff --git a/smack-websocket/src/main/java/org/jivesoftware/smack/websocket/XmppWebSocketTransportModule.java b/smack-websocket/src/main/java/org/jivesoftware/smack/websocket/XmppWebSocketTransportModule.java index 9cfa8fe2f..55236858c 100644 --- a/smack-websocket/src/main/java/org/jivesoftware/smack/websocket/XmppWebSocketTransportModule.java +++ b/smack-websocket/src/main/java/org/jivesoftware/smack/websocket/XmppWebSocketTransportModule.java @@ -135,7 +135,7 @@ public final class XmppWebSocketTransportModule // TODO: It appears this should be done in a generic way. I'd assume we always // have to wait for stream features after the connection was established. But I - // am not yet 100% positive that this is the case for every transport. Hence keep it here for now(?). + // am not yet 100% positive that this is the case for every transport. Hence, keep it here for now(?). // See also similar comment in XmppTcpTransportModule. // Maybe move this into ConnectedButUnauthenticated state's transitionInto() method? That seems to be the // right place. @@ -157,7 +157,7 @@ public final class XmppWebSocketTransportModule final WebSocketRemoteConnectionEndpoint connectedEndpoint; public WebSocketConnectedResult(WebSocketRemoteConnectionEndpoint connectedEndpoint) { - super("WebSocket connection establised with endpoint: " + connectedEndpoint); + super("WebSocket connection established with endpoint: " + connectedEndpoint); this.connectedEndpoint = connectedEndpoint; } } @@ -271,6 +271,7 @@ public final class XmppWebSocketTransportModule asyncButOrderedOutgoingElementsQueue.performAsyncButOrdered(outgoingElementsQueue, () -> { for (TopLevelStreamElement topLevelStreamElement; (topLevelStreamElement = outgoingElementsQueue.poll()) != null;) { websocket.send(topLevelStreamElement); + connectionInternal.fireFirstLevelElementSendListeners(topLevelStreamElement); } }); } diff --git a/smack-websocket/src/main/java/org/jivesoftware/smack/websocket/rce/WebSocketRemoteConnectionEndpointLookup.java b/smack-websocket/src/main/java/org/jivesoftware/smack/websocket/rce/WebSocketRemoteConnectionEndpointLookup.java index 347180f69..658814916 100644 --- a/smack-websocket/src/main/java/org/jivesoftware/smack/websocket/rce/WebSocketRemoteConnectionEndpointLookup.java +++ b/smack-websocket/src/main/java/org/jivesoftware/smack/websocket/rce/WebSocketRemoteConnectionEndpointLookup.java @@ -77,7 +77,7 @@ public final class WebSocketRemoteConnectionEndpointLookup { public Result(List lookupFailures) { // The list of endpoints needs to be mutable, because maybe a user supplied endpoint will be added to it. - // Hence we do not use Collections.emptyList() as argument for the discovered endpoints. + // Hence, we do not use Collections.emptyList() as argument for the discovered endpoints. this(new ArrayList<>(1), new ArrayList<>(1), lookupFailures); } @@ -99,7 +99,7 @@ public final class WebSocketRemoteConnectionEndpointLookup { // TODO: Remove the following methods since the fields are already public? Or make the fields private and use // the methods? I tend to remove the methods, as their method name is pretty long. But OTOH the fields reference - // mutable datastructes, which is uncommon to be public. + // mutable datastructures, which is uncommon to be public. public List getDiscoveredSecureRemoteConnectionEndpoints() { return discoveredSecureEndpoints; } diff --git a/smack-xmlparser-stax/build.gradle b/smack-xmlparser-stax/build.gradle index b25ce2e9a..9cc67deda 100644 --- a/smack-xmlparser-stax/build.gradle +++ b/smack-xmlparser-stax/build.gradle @@ -1,7 +1,6 @@ -// Note that this is also declared in the main build.gradle for -// subprojects, but since evaluationDependsOnChildren is enabled we -// need to declare it here too to have bundle{bnd{...}} available -apply plugin: 'biz.aQute.bnd.builder' +plugins { + id 'org.igniterealtime.smack.java-common-conventions' +} description = """\ Smack XML parser using Stax.""" diff --git a/smack-xmlparser-xpp3/build.gradle b/smack-xmlparser-xpp3/build.gradle index f0a9f56c6..8c5063117 100644 --- a/smack-xmlparser-xpp3/build.gradle +++ b/smack-xmlparser-xpp3/build.gradle @@ -1,7 +1,7 @@ -// Note that this is also declared in the main build.gradle for -// subprojects, but since evaluationDependsOnChildren is enabled we -// need to declare it here too to have bundle{bnd{...}} available -apply plugin: 'biz.aQute.bnd.builder' +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} description = """\ Smack XML parser using XPP3.""" diff --git a/smack-xmlparser/build.gradle b/smack-xmlparser/build.gradle index 513a2b153..9686ae87b 100644 --- a/smack-xmlparser/build.gradle +++ b/smack-xmlparser/build.gradle @@ -1,7 +1,7 @@ -// Note that this is also declared in the main build.gradle for -// subprojects, but since evaluationDependsOnChildren is enabled we -// need to declare it here too to have bundle{bnd{...}} available -apply plugin: 'biz.aQute.bnd.builder' +plugins { + id 'org.igniterealtime.smack.java-common-conventions' + id 'org.igniterealtime.smack.android-conventions' +} description = """\ Smack XML parser fundamentals""" diff --git a/smack-xmlparser/src/main/java/org/jivesoftware/smack/xml/XmlPullParser.java b/smack-xmlparser/src/main/java/org/jivesoftware/smack/xml/XmlPullParser.java index c49ac66ae..fd5a5acfc 100644 --- a/smack-xmlparser/src/main/java/org/jivesoftware/smack/xml/XmlPullParser.java +++ b/smack-xmlparser/src/main/java/org/jivesoftware/smack/xml/XmlPullParser.java @@ -110,7 +110,7 @@ public interface XmlPullParser { String getAttributeNamespace(int index); /** - * Returns the loacalpart of the attribute's name or null in case the index does not refer to an + * Returns the localpart of the attribute's name or null in case the index does not refer to an * attribute. * * @param index the attribute index. diff --git a/version b/version index 0c77728e0..da70b5b12 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.5.0-alpha3-SNAPSHOT +4.5.0-beta5-SNAPSHOT