mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-12-01 16:32:08 +01:00
Compare commits
7 commits
4dd3800d82
...
12e20c2d6c
Author | SHA1 | Date | |
---|---|---|---|
|
12e20c2d6c | ||
|
0cbbac90bc | ||
|
6029ba875b | ||
e35bdb1da2 | |||
3fb4438566 | |||
|
cc636fff21 | ||
|
cec312fe64 |
153 changed files with 7397 additions and 4703 deletions
|
@ -18,7 +18,7 @@ Getting started
|
|||
|
||||
Start with having a look at the **[Documentation]** and the **[Javadoc]**.
|
||||
|
||||
Instructions how to use Smack in your Java or Android project are provided in the [Smack Readme and Upgrade Guide](https://igniterealtime.org/projects/smack/readme).
|
||||
Instructions on how to use Smack in your Java or Android project are provided in the [Smack Readme and Upgrade Guide](https://igniterealtime.org/projects/smack/readme).
|
||||
|
||||
Professional Services
|
||||
---------------------
|
||||
|
|
60
build.gradle
60
build.gradle
|
@ -14,6 +14,8 @@ buildscript {
|
|||
plugins {
|
||||
id 'ru.vyarus.animalsniffer' version '1.5.0'
|
||||
id 'net.ltgt.errorprone' version '1.1.1'
|
||||
// Use e.g. "gradle <task> taskTree" to show its dependency tree.
|
||||
id 'com.dorongold.task-tree' version '1.5'
|
||||
}
|
||||
|
||||
apply plugin: 'org.kordamp.gradle.markdown'
|
||||
|
@ -288,14 +290,7 @@ configure (junit4Projects) {
|
|||
}
|
||||
}
|
||||
|
||||
task copyAllJavadocDocFiles(type: Copy) {
|
||||
from javadocAllProjects.collect { project ->
|
||||
"${project.projectDir}/src/javadoc" }
|
||||
into javadocAllDir
|
||||
include '**/doc-files/*.*'
|
||||
}
|
||||
|
||||
task javadocAll(type: Javadoc, dependsOn: copyAllJavadocDocFiles) {
|
||||
task javadocAll(type: Javadoc) {
|
||||
source javadocAllProjects.collect {project ->
|
||||
project.sourceSets.main.allJava.findAll {
|
||||
// Filter out symbolic links to avoid
|
||||
|
@ -325,6 +320,25 @@ task javadocAll(type: Javadoc, dependsOn: copyAllJavadocDocFiles) {
|
|||
] as String[]
|
||||
overview = "$projectDir/resources/javadoc-overview.html"
|
||||
}
|
||||
|
||||
// Finally copy the javadoc doc-files from the subprojects, which
|
||||
// are potentially generated, to the javadocAll directory. Note
|
||||
// that we use a copy *method* and not a *task* because the inputs
|
||||
// of copy tasks is determined within the configuration phase. And
|
||||
// since some of the inputs are generated, they will not get
|
||||
// picked up if we used a copy method. See also
|
||||
// https://stackoverflow.com/a/40518516/194894
|
||||
doLast {
|
||||
copy {
|
||||
javadocAllProjects.each {
|
||||
from ("${it.projectDir}/src/javadoc") {
|
||||
include '**/doc-files/*.*'
|
||||
}
|
||||
}
|
||||
|
||||
into javadocAllDir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
@ -494,31 +508,25 @@ subprojects {
|
|||
}
|
||||
|
||||
// Work around https://github.com/gradle/gradle/issues/4046
|
||||
javadoc.dependsOn('copyJavadocDocFiles')
|
||||
task copyJavadocDocFiles(type: Copy) {
|
||||
from('src/javadoc')
|
||||
into 'build/docs/javadoc'
|
||||
include '**/doc-files/*.*'
|
||||
}
|
||||
javadoc.dependsOn copyJavadocDocFiles
|
||||
|
||||
// If this subproject has a Makefile then make copyJavadocDocFiles
|
||||
// and the root project's javadocAll task dependend on
|
||||
// generateFiles.
|
||||
if (file("$projectDir/Makefile").exists()) {
|
||||
copyJavadocDocFiles.dependsOn('generateFiles')
|
||||
rootProject.copyAllJavadocDocFiles.dependsOn("${project.name}:generateFiles")
|
||||
task generateFiles(type: Exec) {
|
||||
workingDir projectDir
|
||||
commandLine 'make'
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
||||
clean.dependsOn('cleanGeneratedFiles')
|
||||
rootProject.clean.dependsOn("${project.name}:cleanGeneratedFiles")
|
||||
task cleanGeneratedFiles(type: Exec) {
|
||||
workingDir projectDir
|
||||
commandLine 'make', 'clean'
|
||||
}
|
||||
}
|
||||
// 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) {
|
||||
|
|
35
documentation/connection-modules.md
Normal file
35
documentation/connection-modules.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
Smack's Modular Connection Architecture
|
||||
======================================
|
||||
|
||||
[Back](index.md)
|
||||
|
||||
**Note: Everything related to the modular connection architecture is currently considered experimental and should not be used in production. Use the mature `XMPPTCPConnection` if you do not feel adventurous.
|
||||
|
||||
Smack's modular connection architecture allows to extend a XMPP c2s (client-to-server) connection with additional functionalty by adding modules.
|
||||
Those modules extend the Finite State Machine (FSM) within the `ModularXmppClientToServerConnection` with new states.
|
||||
|
||||
Connection modules can either be
|
||||
- Transports
|
||||
- Extensions
|
||||
|
||||
Transports bind the XMPP XML stream to an underlying transport like TCP, WebSockets, BOSH, and allow for the different particularities of transports like DirectTLS ([XEP-0368](https://xmpp.org/extensions/xep-0368.html)).
|
||||
This eventually means that a single transport module can implement multiple transport mechanisms.
|
||||
For example the TCP transport module implements the RFC6120 TCP and the XEP-0368 direct TLS TCP transport bindings.
|
||||
|
||||
Extensions allow for a richer functionality of the connection. Those include
|
||||
- Compression
|
||||
- zlib ([XEP-0138](https://xmpp.org/extensions/xep-0138.html))
|
||||
- [Efficient XML Interchange (EXI)](https://www.w3.org/TR/exi/)
|
||||
- Instant Stream Resumption ([XEP-0397](https://xmpp.org/extensions/xep-0397.html)
|
||||
- Bind2
|
||||
- Stream Management
|
||||
|
||||
Note that not all extensions work with every transport.
|
||||
For example compression only works with TCP-based transport bindings.
|
||||
|
||||
|
||||
Connection modules are plugged into the the modular connection via their constructor. and they usually declare backwards edges to some common, generic connection state of the FSM.
|
||||
|
||||
Modules and states always have an accompanying *descriptor* type.
|
||||
`ModuleDescriptor` and `StateDescriptor` exist without an connection instance.
|
||||
They describe the module and state metadata, while their modules are states are instanciated once a modular connection is instanciated.
|
|
@ -58,8 +58,8 @@ debugger=console
|
|||
|
||||
### Framework properties
|
||||
|
||||
| Name | |
|
||||
|----------------------|-------------------------------------------|
|
||||
| Name | Description |
|
||||
|----------------------|-----------------------------------------------------------------------------|
|
||||
| service | XMPP service to run the tests on |
|
||||
| serviceTlsPin | TLS Pin (used by [java-pinning](https://github.com/Flowdalic/java-pinning)) |
|
||||
| securityMode | Either 'required' or 'disabled' |
|
||||
|
@ -75,7 +75,11 @@ debugger=console
|
|||
| debugger | 'console' for console debugger, 'enhanced' for the enhanced debugger |
|
||||
| enabledTests | List of enabled tests |
|
||||
| disabledTests | List of disabled tests |
|
||||
| defaultConnection | Nickname of the default connection |
|
||||
| enabledConnections | List of enabled connection's nicknames |
|
||||
| disabledConnections | List of disabled connection's nicknames |
|
||||
| testPackages | List of packages with tests |
|
||||
| verbose | If `true` set output to verbose |
|
||||
|
||||
### Where to place the properties file
|
||||
|
||||
|
@ -99,6 +103,10 @@ The base class that integration tests need to subclass.
|
|||
|
||||
Allows low level integration test, i.e. ever test method will have its on exclusive XMPPTCPConnection instances.
|
||||
|
||||
### `AbstractSmackSpecificLowLevelIntegrationTest`
|
||||
|
||||
Operates, like `AbstractSmackLowLevelIntegrationTest` on its own `XMPPConnection` instances, but is limited to a particular type of `XMPPConnection`.
|
||||
|
||||
### `IntegrationTestEnvironment`
|
||||
|
||||
The environment, e.g. the `XMPPConnections` provided to the integration tests by the framework. Note that for convenience `AbstractSmackIntegrationTest` contains some of those as protected members.
|
||||
|
|
|
@ -23,6 +23,13 @@ Currently supported XEPs of smack-tcp
|
|||
|---------------------------------------------|--------------------------------------------------------|-----------|----------------------------------------------------------------------------------------------------------|
|
||||
| [Stream Management](streammanagement.md) | [XEP-0198](https://xmpp.org/extensions/xep-0198.html) | n/a | Allows active management of an XML Stream between two XMPP entities (stanza acknowledgement, stream resumption). |
|
||||
|
||||
Currently supported XEPs of smack-im
|
||||
------------------------------------
|
||||
|
||||
| Name | XEP | Version | Description |
|
||||
|---------------------------------------------|--------------------------------------------------------|-----------|-----------------------------------|--
|
||||
| Roster Versioning | [XEP-0237](https://xmpp.org/extensions/xep-0237.html) | n/a | Efficient roster synchronization. |
|
||||
|
||||
Smack Extensions and currently supported XEPs of smack-extensions
|
||||
-----------------------------------------------------------------
|
||||
|
||||
|
@ -52,17 +59,19 @@ Smack Extensions and currently supported XEPs of smack-extensions
|
|||
| Chat State Notifications | [XEP-0085](https://xmpp.org/extensions/xep-0085.html) | n/a | Communicating the status of a user in a chat session. |
|
||||
| [Time Exchange](time.md) | [XEP-0090](https://xmpp.org/extensions/xep-0090.html) | n/a | Allows local time information to be shared between users. |
|
||||
| Software Version | [XEP-0092](https://xmpp.org/extensions/xep-0092.html) | n/a | Retrieve and announce the software application of an XMPP entity. |
|
||||
| Stream Initation | [XEP-0095](https://xmpp.org/extensions/xep-0095.html) | n/a | Initiating a data stream between any two XMPP entities. |
|
||||
| Stream Initiation | [XEP-0095](https://xmpp.org/extensions/xep-0095.html) | n/a | Initiating a data stream between any two XMPP entities. |
|
||||
| [SI File Transfer](filetransfer.md) | [XEP-0096](https://xmpp.org/extensions/xep-0096.html) | n/a | Transfer files between two users over XMPP. |
|
||||
| User Mood | [XEP-0107](https://xmpp.org/extensions/xep-0107.html) | 1.2.1 | Communicate the users current mood. |
|
||||
| [Entity Capabilities](caps.md) | [XEP-0115](https://xmpp.org/extensions/xep-0115.html) | n/a | Broadcasting and dynamic discovery of entity capabilities. |
|
||||
| User Tune | [XEP-0118](https://xmpp.org/extensions/xep-0118.html) | n/a | Defines a payload format for communicating information about music to which a user is listening. |
|
||||
| Data Forms Validation | [XEP-0122](https://xmpp.org/extensions/xep-0122.html) | n/a | Enables an application to specify additional validation guidelines . |
|
||||
| Stanza Headers and Internet Metadata (SHIM) | [XEP-0131](https://xmpp.org/extensions/xep-0131.html) | 1.2 | Add Metadata Headers to Stanzas. |
|
||||
| Service Administration | [XEP-0133](https://xmpp.org/extensions/xep-0133.html) | n/a | Recommended best practices for service-level administration of servers and components using Ad-Hoc Commands. |
|
||||
| Stream Compression | [XEP-0138](https://xmpp.org/extensions/xep-0138.html) | n/a | Support for optional compression of the XMPP stream.
|
||||
| Data Forms Layout | [XEP-0141](https://xmpp.org/extensions/xep-0141.html) | n/a | Enables an application to specify form layouts. |
|
||||
| Personal Eventing Protocol | [XEP-0163](https://xmpp.org/extensions/xep-0163.html) | n/a | Using the XMPP publish-subscribe protocol to broadcast state change events associated with an XMPP account. |
|
||||
| [Jingle](jingle.html) | [XEP-0166](https://xmpp.org/extensions/xep-0166.html) | n/a | Initiate and manage sessions between two XMPP entities. |
|
||||
| User Nickname | [XEP-0172](https://xmpp.org/extensions/xep-0172.html) | n/a | Communicate user nicknames. |
|
||||
| Message Delivery Receipts | [XEP-0184](https://xmpp.org/extensions/xep-0184.html) | n/a | Extension for message delivery receipts. The sender can request notification that the message has been delivered. |
|
||||
| [Blocking Command](blockingcommand.md) | [XEP-0191](https://xmpp.org/extensions/xep-0191.html) | n/a | Communications blocking that is intended to be simpler than privacy lists (XEP-0016). |
|
||||
| XMPP Ping | [XEP-0199](https://xmpp.org/extensions/xep-0199.html) | n/a | Sending application-level pings over XML streams.
|
||||
|
@ -73,6 +82,7 @@ Smack Extensions and currently supported XEPs of smack-extensions
|
|||
| Attention | [XEP-0224](https://xmpp.org/extensions/xep-0224.html) | n/a | Getting attention of another user. |
|
||||
| Bits of Binary | [XEP-0231](https://xmpp.org/extensions/xep-0231.html) | n/a | Including or referring to small bits of binary data in an XML stanza. |
|
||||
| Best Practices for Resource Locking | [XEP-0296](https://xmpp.org/extensions/xep-0296.html) | n/a | Specifies best practices to be followed by Jabber/XMPP clients about when to lock into, and unlock away from, resources. |
|
||||
| Stanza Forwarding | [XEP-0297](https://xmpp.org/extensions/xep-0297.html) | n/a | Allows forwarding of Stanzas. |
|
||||
| Last Message Correction | [XEP-0308](https://xmpp.org/extensions/xep-0308.html) | n/a | Provides a method for indicating that a message is a correction of the last sent message. |
|
||||
| Last User Interaction in Presence | [XEP-0319](https://xmpp.org/extensions/xep-0319.html) | n/a | Communicate time of last user interaction via XMPP presence notifications. |
|
||||
| Data Forms Geolocation Element | [XEP-0350](https://xmpp.org/extensions/xep-0350.html) | n/a | Allows to include XEP-0080 gelocation data in XEP-0004 data forms. |
|
||||
|
@ -102,6 +112,7 @@ Experimental Smack Extensions and currently supported XEPs of smack-experimental
|
|||
| Stable and Unique Stanza IDs | [XEP-0359](https://xmpp.org/extensions/xep-0359.html) | 0.5.0 | This specification describes unique and stable IDs for messages. |
|
||||
| HTTP File Upload | [XEP-0363](https://xmpp.org/extensions/xep-0363.html) | 0.3.1 | Protocol to request permissions to upload a file to an HTTP server and get a shareable URL. |
|
||||
| References | [XEP-0372](https://xmpp.org/extensions/xep-0363.html) | 0.2.0 | Add references like mentions or external data to stanzas. |
|
||||
| Explicit Message Encryption | [XEP-0380](https://xmpp.org/extensions/xep-0380.html) | 0.3.0 | Mark a message as explicitly encrypted. |
|
||||
| [OpenPGP for XMPP](ox.md) | [XEP-0373](https://xmpp.org/extensions/xep-0373.html) | 0.3.2 | Utilize OpenPGP to exchange encrypted and signed content. |
|
||||
| [OpenPGP for XMPP: Instant Messaging](ox-im.md) | [XEP-0374](https://xmpp.org/extensions/xep-0374.html) | 0.2.0 | OpenPGP encrypted Instant Messaging. |
|
||||
| [Spoiler Messages](spoiler.md) | [XEP-0382](https://xmpp.org/extensions/xep-0382.html) | 0.2.0 | Indicate that the body of a message should be treated as a spoiler. |
|
||||
|
|
|
@ -13,3 +13,4 @@
|
|||
* [Debugging with Smack](debugging.md)
|
||||
|
||||
* [Smack Extensions Manual](extensions/index.md)
|
||||
* [Smack's Modular Connection Architecture](connection-modules.md)
|
||||
|
|
|
@ -67,6 +67,7 @@ SMACK_EXCEPTIONS[CorruptedOmemoKeyException]="if the OMEMO key is corrupted."
|
|||
SMACK_EXCEPTIONS[CryptoFailedException]="if the OMEMO cryptography failed."
|
||||
SMACK_EXCEPTIONS[CannotEstablishOmemoSessionException]="if no OMEMO session could be established."
|
||||
SMACK_EXCEPTIONS[UntrustedOmemoIdentityException]="if the OMEMO identity is not trusted."
|
||||
SMACK_EXCEPTIONS[FailedNonzaException]="if an XMPP protocol failure was received."
|
||||
|
||||
MODE=""
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ include 'smack-core',
|
|||
'smack-android',
|
||||
'smack-android-extensions',
|
||||
'smack-java7',
|
||||
'smack-java8-full',
|
||||
'smack-integration-test',
|
||||
'smack-omemo',
|
||||
'smack-omemo-signal',
|
||||
|
|
|
@ -27,7 +27,7 @@ import java.util.logging.Logger;
|
|||
|
||||
import org.jivesoftware.smack.AbstractXMPPConnection;
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.SmackException.ConnectionException;
|
||||
import org.jivesoftware.smack.SmackException.GenericConnectionException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackException.SmackWrappedException;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
|
@ -136,6 +136,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
|
|||
this.config = config;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
protected void connectInternal() throws SmackException, InterruptedException {
|
||||
done = false;
|
||||
|
@ -177,7 +178,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
|
|||
.setAttribute(BodyQName.createWithPrefix(XMPP_BOSH_NS, "version", "xmpp"), "1.0")
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
throw new ConnectionException(e);
|
||||
throw new GenericConnectionException(e);
|
||||
}
|
||||
|
||||
// Wait for the response from the server
|
||||
|
|
|
@ -25,6 +25,8 @@ dependencies {
|
|||
testCompile "org.xmlunit:xmlunit-assertj:$xmlUnitVersion"
|
||||
testCompile 'com.jamesmurty.utils:java-xmlbuilder:1.2'
|
||||
testCompile 'org.bouncycastle:bcprov-jdk15on:1.64'
|
||||
testCompile 'com.google.guava:guava:28.2-jre'
|
||||
testCompile 'org.jgrapht:jgrapht-io:1.3.1'
|
||||
}
|
||||
|
||||
class CreateFileTask extends DefaultTask {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2009 Jive Software, 2018-2019 Florian Schmaus.
|
||||
* Copyright 2009 Jive Software, 2018-2020 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,7 +34,6 @@ import java.security.SecureRandom;
|
|||
import java.security.Security;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
@ -87,6 +86,7 @@ import org.jivesoftware.smack.XMPPException.StreamErrorException;
|
|||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||
import org.jivesoftware.smack.compress.packet.Compress;
|
||||
import org.jivesoftware.smack.compression.XMPPInputOutputStream;
|
||||
import org.jivesoftware.smack.datatypes.UInt16;
|
||||
import org.jivesoftware.smack.debugger.SmackDebugger;
|
||||
import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
|
||||
import org.jivesoftware.smack.filter.IQReplyFilter;
|
||||
|
@ -136,7 +136,6 @@ import org.jivesoftware.smack.util.ParserUtils;
|
|||
import org.jivesoftware.smack.util.Predicate;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.TLSUtils;
|
||||
import org.jivesoftware.smack.util.dns.HostAddress;
|
||||
import org.jivesoftware.smack.util.dns.SmackDaneProvider;
|
||||
import org.jivesoftware.smack.util.dns.SmackDaneVerifier;
|
||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||
|
@ -150,8 +149,6 @@ import org.jxmpp.jid.impl.JidCreate;
|
|||
import org.jxmpp.jid.parts.Resourcepart;
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
import org.jxmpp.util.XmppStringUtils;
|
||||
import org.minidns.dnsname.DnsName;
|
||||
|
||||
|
||||
/**
|
||||
* This abstract class is commonly used as super class for XMPP connection mechanisms like TCP and BOSH. Hence it
|
||||
|
@ -394,7 +391,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
/**
|
||||
* The used port to establish the connection to
|
||||
*/
|
||||
protected int port;
|
||||
protected UInt16 port;
|
||||
|
||||
/**
|
||||
* Flag that indicates if the user is currently authenticated with the server.
|
||||
|
@ -484,7 +481,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return port;
|
||||
final UInt16 port = this.port;
|
||||
if (port == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return port.intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -525,6 +527,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
saslFeatureReceived.init();
|
||||
lastFeaturesReceived.init();
|
||||
tlsHandled.init();
|
||||
closingStreamReceived.init();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -778,38 +781,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
|
||||
private DomainBareJid xmppServiceDomain;
|
||||
|
||||
protected List<HostAddress> hostAddresses;
|
||||
|
||||
/**
|
||||
* Populates {@link #hostAddresses} with the resolved addresses or with the configured host address. If no host
|
||||
* address was configured and all lookups failed, for example with NX_DOMAIN, then {@link #hostAddresses} will be
|
||||
* populated with the empty list.
|
||||
*
|
||||
* @return a list of host addresses where DNS (SRV) RR resolution failed.
|
||||
*/
|
||||
protected List<HostAddress> populateHostAddresses() {
|
||||
List<HostAddress> failedAddresses = new LinkedList<>();
|
||||
if (config.hostAddress != null) {
|
||||
hostAddresses = new ArrayList<>(1);
|
||||
HostAddress hostAddress = new HostAddress(config.port, config.hostAddress);
|
||||
hostAddresses.add(hostAddress);
|
||||
}
|
||||
else if (config.host != null) {
|
||||
hostAddresses = new ArrayList<>(1);
|
||||
HostAddress hostAddress = DNSUtil.getDNSResolver().lookupHostAddress(config.host, config.port, failedAddresses, config.getDnssecMode());
|
||||
if (hostAddress != null) {
|
||||
hostAddresses.add(hostAddress);
|
||||
}
|
||||
} else {
|
||||
// N.B.: Important to use config.serviceName and not AbstractXMPPConnection.serviceName
|
||||
DnsName dnsName = DnsName.from(config.getXMPPServiceDomain());
|
||||
hostAddresses = DNSUtil.resolveXMPPServiceDomain(dnsName, failedAddresses, config.getDnssecMode());
|
||||
}
|
||||
// Either the populated host addresses are not empty *or* there must be at least one failed address.
|
||||
assert !hostAddresses.isEmpty() || !failedAddresses.isEmpty();
|
||||
return failedAddresses;
|
||||
}
|
||||
|
||||
protected Lock getConnectionLock() {
|
||||
return connectionLock;
|
||||
}
|
||||
|
@ -980,6 +951,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
tlsHandled.reportGenericFailure(smackWrappedException);
|
||||
saslFeatureReceived.reportGenericFailure(smackWrappedException);
|
||||
lastFeaturesReceived.reportGenericFailure(smackWrappedException);
|
||||
closingStreamReceived.reportFailure(smackWrappedException);
|
||||
// TODO From XMPPTCPConnection. Was called in Smack 4.3 where notifyConnectionError() was part of
|
||||
// XMPPTCPConnection. Create delegation method?
|
||||
// maybeCompressFeaturesReceived.reportGenericFailure(smackWrappedException);
|
||||
|
@ -2182,6 +2154,10 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
CACHED_EXECUTOR_SERVICE.execute(runnable);
|
||||
}
|
||||
|
||||
protected final SmackReactor getReactor() {
|
||||
return SMACK_REACTOR;
|
||||
}
|
||||
|
||||
protected static ScheduledAction schedule(Runnable runnable, long delay, TimeUnit unit) {
|
||||
return SMACK_REACTOR.schedule(runnable, delay, unit);
|
||||
}
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Florian Schmaus
|
||||
*
|
||||
* 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;
|
||||
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
|
||||
import org.jivesoftware.smack.SmackReactor.ChannelSelectedCallback;
|
||||
import org.jivesoftware.smack.fsm.AbstractXmppStateMachineConnection;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptor;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptorGraph.GraphVertex;
|
||||
|
||||
public abstract class AbstractXmppNioConnection extends AbstractXmppStateMachineConnection {
|
||||
|
||||
protected AbstractXmppNioConnection(ConnectionConfiguration configuration, GraphVertex<StateDescriptor> initialStateDescriptorVertex) {
|
||||
super(configuration, initialStateDescriptorVertex);
|
||||
}
|
||||
|
||||
protected SelectionKey registerWithSelector(SelectableChannel channel, int ops, ChannelSelectedCallback callback)
|
||||
throws ClosedChannelException {
|
||||
return SMACK_REACTOR.registerWithSelector(channel, ops, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the interest Ops of a SelectionKey. Since Java's NIO interestOps(int) can block at any time, we use a queue
|
||||
* to perform the actual operation in the reactor where we can perform this operation non-blocking.
|
||||
*
|
||||
* @param selectionKey TODO javadoc me please
|
||||
* @param interestOps TODO javadoc me please
|
||||
*/
|
||||
protected void setInterestOps(SelectionKey selectionKey, int interestOps) {
|
||||
SMACK_REACTOR.setInterestOps(selectionKey, interestOps);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2003-2007 Jive Software, 2017-2019 Florian Schmaus.
|
||||
* Copyright 2003-2007 Jive Software, 2017-2020 Florian Schmaus.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -229,14 +229,18 @@ public abstract class ConnectionConfiguration {
|
|||
|
||||
}
|
||||
|
||||
DnsName getHost() {
|
||||
public DnsName getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
InetAddress getHostAddress() {
|
||||
public InetAddress getHostAddress() {
|
||||
return hostAddress;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server name of the target server.
|
||||
*
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2003-2007 Jive Software.
|
||||
* Copyright 2003-2007 Jive Software, 2018-2020 Florian Schmaus.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -28,6 +28,8 @@ import java.util.Set;
|
|||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionModuleDescriptor;
|
||||
import org.jivesoftware.smack.compression.XMPPInputOutputStream;
|
||||
import org.jivesoftware.smack.debugger.ReflectionDebuggerFactory;
|
||||
import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
|
||||
|
@ -238,18 +240,6 @@ public final class SmackConfiguration {
|
|||
compressionHandlers.add(xmppInputOutputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get compression handlers.
|
||||
*
|
||||
* @return a list of compression handlers.
|
||||
* @deprecated use {@link #getCompressionHandlers()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
// TODO: Remove in Smack 4.4.
|
||||
public static List<XMPPInputOutputStream> getCompresionHandlers() {
|
||||
return getCompressionHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get compression handlers.
|
||||
*
|
||||
|
@ -379,4 +369,19 @@ public final class SmackConfiguration {
|
|||
return defaultConcurrencyLevelLimit;
|
||||
}
|
||||
|
||||
private static final Set<Class<? extends ModularXmppClientToServerConnectionModuleDescriptor>> KNOWN_MODULES = new HashSet<>();
|
||||
|
||||
public static boolean addModule(Class<? extends ModularXmppClientToServerConnectionModuleDescriptor> moduleDescriptor) {
|
||||
synchronized (KNOWN_MODULES) {
|
||||
return KNOWN_MODULES.add(moduleDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addAllKnownModulesTo(ModularXmppClientToServerConnectionConfiguration.Builder builder) {
|
||||
synchronized (KNOWN_MODULES) {
|
||||
for (Class<? extends ModularXmppClientToServerConnectionModuleDescriptor> moduleDescriptor : KNOWN_MODULES) {
|
||||
builder.addModule(moduleDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014-2019 Florian Schmaus
|
||||
* Copyright 2014-2020 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,11 +16,15 @@
|
|||
*/
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.jivesoftware.smack.c2s.XmppClientToServerTransport.LookupConnectionEndpointsFailed;
|
||||
import org.jivesoftware.smack.filter.StanzaFilter;
|
||||
import org.jivesoftware.smack.util.dns.HostAddress;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpoint;
|
||||
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure;
|
||||
import org.jivesoftware.smack.util.rce.RemoteConnectionException;
|
||||
|
||||
import org.jxmpp.jid.Jid;
|
||||
|
||||
|
@ -90,16 +94,10 @@ public abstract class SmackException extends Exception {
|
|||
public static NoResponseException newWith(XMPPConnection connection, String waitingFor) {
|
||||
final StringBuilder sb = getWaitingFor(connection);
|
||||
sb.append(" While waiting for ").append(waitingFor);
|
||||
sb.append(" [").append(connection).append(']');
|
||||
return new NoResponseException(sb.toString());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
// TODO: Remove in Smack 4.4.
|
||||
public static NoResponseException newWith(long timeout,
|
||||
StanzaCollector collector) {
|
||||
return newWith(timeout, collector.getStanzaFilter(), false);
|
||||
}
|
||||
|
||||
public static NoResponseException newWith(long timeout,
|
||||
StanzaCollector collector, boolean stanzaCollectorCancelled) {
|
||||
return newWith(timeout, collector.getStanzaFilter(), stanzaCollectorCancelled);
|
||||
|
@ -264,45 +262,112 @@ public abstract class SmackException extends Exception {
|
|||
}
|
||||
}
|
||||
|
||||
public abstract static class ConnectionException extends SmackException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected ConnectionException(Throwable wrappedThrowable) {
|
||||
super(wrappedThrowable);
|
||||
}
|
||||
|
||||
protected ConnectionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static final class GenericConnectionException extends ConnectionException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* ConnectionException is thrown if Smack is unable to connect to all hosts of a given XMPP
|
||||
* service. The failed hosts can be retrieved with
|
||||
* {@link ConnectionException#getFailedAddresses()}, which will have the exception causing the
|
||||
* connection failure set and retrievable with {@link HostAddress#getExceptions()}.
|
||||
* Deprecated, do not use.
|
||||
*
|
||||
* @param wrappedThrowable the wrapped throwable.
|
||||
*/
|
||||
public static class ConnectionException extends SmackException {
|
||||
@Deprecated
|
||||
public GenericConnectionException(Throwable wrappedThrowable) {
|
||||
super(wrappedThrowable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This exception is thrown if Smack is unable to connect to all hosts of a given XMPP
|
||||
* service. The connection exceptions can be retrieved with
|
||||
* {@link EndpointConnectionException#getConnectionExceptions()}, which will have the exception causing the
|
||||
* connection failure set and retrievable with {@link RemoteConnectionException#getException()}.
|
||||
*/
|
||||
public static final class EndpointConnectionException extends ConnectionException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1686944201672697996L;
|
||||
private static final long serialVersionUID = 1;
|
||||
|
||||
private final List<HostAddress> failedAddresses;
|
||||
private final List<RemoteConnectionEndpointLookupFailure> lookupFailures;
|
||||
private final List<? extends RemoteConnectionException<?>> connectionExceptions;
|
||||
|
||||
public ConnectionException(Throwable wrappedThrowable) {
|
||||
super(wrappedThrowable);
|
||||
failedAddresses = new ArrayList<>(0);
|
||||
}
|
||||
|
||||
private ConnectionException(String message, List<HostAddress> failedAddresses) {
|
||||
private EndpointConnectionException(String message, List<RemoteConnectionEndpointLookupFailure> lookupFailures,
|
||||
List<? extends RemoteConnectionException<?>> connectionExceptions) {
|
||||
super(message);
|
||||
this.failedAddresses = failedAddresses;
|
||||
// At least one list must contain an entry.
|
||||
assert !lookupFailures.isEmpty() || !connectionExceptions.isEmpty();
|
||||
this.lookupFailures = lookupFailures;
|
||||
this.connectionExceptions = connectionExceptions;
|
||||
}
|
||||
|
||||
public static ConnectionException from(List<HostAddress> failedAddresses) {
|
||||
final String DELIMITER = ", ";
|
||||
StringBuilder sb = new StringBuilder("The following addresses failed: ");
|
||||
for (HostAddress hostAddress : failedAddresses) {
|
||||
sb.append(hostAddress.getErrorMessage());
|
||||
sb.append(DELIMITER);
|
||||
}
|
||||
// Remove the last delimiter
|
||||
sb.setLength(sb.length() - DELIMITER.length());
|
||||
return new ConnectionException(sb.toString(), failedAddresses);
|
||||
public static EndpointConnectionException from(List<RemoteConnectionEndpointLookupFailure> lookupFailures,
|
||||
List<? extends RemoteConnectionException<?>> connectionExceptions) {
|
||||
StringBuilder sb = new StringBuilder(256);
|
||||
|
||||
if (!lookupFailures.isEmpty()) {
|
||||
sb.append("Could not lookup the following endpoints: ");
|
||||
StringUtils.appendTo(lookupFailures, sb);
|
||||
}
|
||||
|
||||
public List<HostAddress> getFailedAddresses() {
|
||||
return failedAddresses;
|
||||
if (!connectionExceptions.isEmpty()) {
|
||||
sb.append("The following addresses failed: ");
|
||||
StringUtils.appendTo(connectionExceptions, sb, rce -> sb.append(rce.getErrorMessage()));
|
||||
}
|
||||
|
||||
return new EndpointConnectionException(sb.toString(), lookupFailures, connectionExceptions);
|
||||
}
|
||||
|
||||
public List<RemoteConnectionEndpointLookupFailure> getLookupFailures() {
|
||||
return lookupFailures;
|
||||
}
|
||||
|
||||
public List<? extends RemoteConnectionException<? extends RemoteConnectionEndpoint>> getConnectionExceptions() {
|
||||
return connectionExceptions;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class NoEndpointsDiscoveredException extends ConnectionException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final List<LookupConnectionEndpointsFailed> lookupFailures;
|
||||
|
||||
private NoEndpointsDiscoveredException(String message, List<LookupConnectionEndpointsFailed> lookupFailures) {
|
||||
super(message);
|
||||
this.lookupFailures = Collections.unmodifiableList(lookupFailures);
|
||||
}
|
||||
|
||||
public List<LookupConnectionEndpointsFailed> getLookupFailures() {
|
||||
return lookupFailures;
|
||||
}
|
||||
|
||||
public static NoEndpointsDiscoveredException from(List<LookupConnectionEndpointsFailed> lookupFailures) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (lookupFailures.isEmpty()) {
|
||||
sb.append("No endpoint lookup finished within the timeout");
|
||||
} else {
|
||||
sb.append("Not endpoints could be discovered due the following lookup failures: ");
|
||||
StringUtils.appendTo(lookupFailures, sb);
|
||||
}
|
||||
|
||||
return new NoEndpointsDiscoveredException(sb.toString(), lookupFailures);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017-2018 Florian Schmaus
|
||||
* Copyright 2017-2020 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,7 +19,9 @@ package org.jivesoftware.smack;
|
|||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -31,6 +33,7 @@ import javax.net.SocketFactory;
|
|||
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smack.util.CallbackRecipient;
|
||||
import org.jivesoftware.smack.util.Consumer;
|
||||
import org.jivesoftware.smack.util.ExceptionCallback;
|
||||
import org.jivesoftware.smack.util.SuccessCallback;
|
||||
|
||||
|
@ -48,6 +51,8 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
|
|||
|
||||
private ExceptionCallback<E> exceptionCallback;
|
||||
|
||||
private Consumer<SmackFuture<V, E>> completionCallback;
|
||||
|
||||
@Override
|
||||
public final synchronized boolean cancel(boolean mayInterruptIfRunning) {
|
||||
if (isDone()) {
|
||||
|
@ -87,6 +92,11 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
|
|||
return this;
|
||||
}
|
||||
|
||||
public void onCompletion(Consumer<SmackFuture<V, E>> completionCallback) {
|
||||
this.completionCallback = completionCallback;
|
||||
maybeInvokeCallbacks();
|
||||
}
|
||||
|
||||
private V getOrThrowExecutionException() throws ExecutionException {
|
||||
assert result != null || exception != null || cancelled;
|
||||
if (result != null) {
|
||||
|
@ -148,11 +158,19 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
|
|||
return getOrThrowExecutionException();
|
||||
}
|
||||
|
||||
public V getIfAvailable() {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected final synchronized void maybeInvokeCallbacks() {
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((result != null || exception != null) && completionCallback != null) {
|
||||
completionCallback.accept(this);
|
||||
}
|
||||
|
||||
if (result != null && successCallback != null) {
|
||||
AbstractXMPPConnection.asyncGo(new Runnable() {
|
||||
@Override
|
||||
|
@ -308,4 +326,12 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
|
|||
return future;
|
||||
}
|
||||
|
||||
public static boolean await(Collection<? extends SmackFuture<?, ?>> futures, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
CountDownLatch latch = new CountDownLatch(futures.size());
|
||||
for (SmackFuture<?, ?> future : futures) {
|
||||
future.onCompletion(f -> latch.countDown());
|
||||
}
|
||||
|
||||
return latch.await(timeout, unit);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,12 +26,15 @@ import java.util.List;
|
|||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.bind2.Bind2ModuleDescriptor;
|
||||
import org.jivesoftware.smack.compress.provider.CompressedProvider;
|
||||
import org.jivesoftware.smack.compress.provider.FailureProvider;
|
||||
import org.jivesoftware.smack.compression.CompressionModuleDescriptor;
|
||||
import org.jivesoftware.smack.compression.Java7ZlibInputOutputStream;
|
||||
import org.jivesoftware.smack.compression.XmppCompressionManager;
|
||||
import org.jivesoftware.smack.compression.zlib.ZlibXmppCompressionFactory;
|
||||
import org.jivesoftware.smack.initializer.SmackInitializer;
|
||||
import org.jivesoftware.smack.isr.InstantStreamResumptionModuleDescriptor;
|
||||
import org.jivesoftware.smack.packet.Bind;
|
||||
import org.jivesoftware.smack.packet.Message.Body;
|
||||
import org.jivesoftware.smack.provider.BindIQProvider;
|
||||
|
@ -136,6 +139,10 @@ public final class SmackInitialization {
|
|||
ProviderManager.addNonzaProvider(CompressedProvider.INSTANCE);
|
||||
ProviderManager.addNonzaProvider(FailureProvider.INSTANCE);
|
||||
|
||||
SmackConfiguration.addModule(Bind2ModuleDescriptor.class);
|
||||
SmackConfiguration.addModule(CompressionModuleDescriptor.class);
|
||||
SmackConfiguration.addModule(InstantStreamResumptionModuleDescriptor.class);
|
||||
|
||||
SmackConfiguration.smackInitialized = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2019 Florian Schmaus
|
||||
* Copyright 2018-2020 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -116,7 +116,7 @@ public class SmackReactor {
|
|||
setReactorThreadCount(DEFAULT_REACTOR_THREAD_COUNT);
|
||||
}
|
||||
|
||||
SelectionKey registerWithSelector(SelectableChannel channel, int ops, ChannelSelectedCallback callback)
|
||||
public SelectionKey registerWithSelector(SelectableChannel channel, int ops, ChannelSelectedCallback callback)
|
||||
throws ClosedChannelException {
|
||||
SelectionKeyAttachment selectionKeyAttachment = new SelectionKeyAttachment(callback);
|
||||
|
||||
|
@ -129,7 +129,7 @@ public class SmackReactor {
|
|||
}
|
||||
}
|
||||
|
||||
void setInterestOps(SelectionKey selectionKey, int interestOps) {
|
||||
public void setInterestOps(SelectionKey selectionKey, int interestOps) {
|
||||
SetInterestOps setInterestOps = new SetInterestOps(selectionKey, interestOps);
|
||||
pendingSetInterestOps.add(setInterestOps);
|
||||
selector.wakeup();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Florian Schmaus
|
||||
* Copyright 2018-2020 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -70,7 +70,7 @@ public interface XmppInputOutputFilter {
|
|||
default void waitUntilInputOutputClosed() throws IOException, NoResponseException, CertificateException, InterruptedException, SmackException {
|
||||
}
|
||||
|
||||
default Object getStats() {
|
||||
return null;
|
||||
}
|
||||
Object getStats();
|
||||
|
||||
String getFilterName();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2019-2020 Florian Schmaus
|
||||
*
|
||||
* 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.bind2;
|
||||
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.AuthenticatedAndResourceBoundStateDescriptor;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.ConnectedButUnauthenticatedStateDescriptor;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.SaslAuthenticationStateDescriptor;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionModule;
|
||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||
import org.jivesoftware.smack.c2s.internal.WalkStateGraphContext;
|
||||
import org.jivesoftware.smack.fsm.State;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptor;
|
||||
import org.jivesoftware.smack.fsm.StateTransitionResult;
|
||||
|
||||
public class Bind2Module extends ModularXmppClientToServerConnectionModule<Bind2ModuleDescriptor> {
|
||||
|
||||
protected Bind2Module(Bind2ModuleDescriptor moduleDescriptor,
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
super(moduleDescriptor, connectionInternal);
|
||||
}
|
||||
|
||||
public static final class Bind2StateDescriptor extends StateDescriptor {
|
||||
private Bind2StateDescriptor() {
|
||||
super(Bind2State.class, 386, StateDescriptor.Property.notImplemented);
|
||||
|
||||
addPredeccessor(ConnectedButUnauthenticatedStateDescriptor.class);
|
||||
addSuccessor(AuthenticatedAndResourceBoundStateDescriptor.class);
|
||||
declarePrecedenceOver(SaslAuthenticationStateDescriptor.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bind2Module.Bind2State constructState(ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
// This is the trick: the module is constructed prior the states, so we get the actual state out of the module by fetching the module from the connection.
|
||||
Bind2Module bind2Module = connectionInternal.connection.getConnectionModuleFor(Bind2ModuleDescriptor.class);
|
||||
return bind2Module.constructBind2State(this, connectionInternal);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Bind2State extends State {
|
||||
|
||||
private Bind2State(Bind2StateDescriptor bind2StateDescriptor,
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
super(bind2StateDescriptor, connectionInternal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateTransitionResult.TransitionImpossible isTransitionToPossible(WalkStateGraphContext walkStateGraphContext) {
|
||||
return new StateTransitionResult.TransitionImpossibleBecauseNotImplemented(stateDescriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) {
|
||||
throw new IllegalStateException("Bind2 not implemented");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Bind2State constructBind2State(Bind2StateDescriptor bind2StateDescriptor,
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
return new Bind2State(bind2StateDescriptor, connectionInternal);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2019-2020 Florian Schmaus
|
||||
*
|
||||
* 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.bind2;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionModuleDescriptor;
|
||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptor;
|
||||
|
||||
public class Bind2ModuleDescriptor extends ModularXmppClientToServerConnectionModuleDescriptor {
|
||||
|
||||
private static final Bind2ModuleDescriptor INSTANCE = new Bind2ModuleDescriptor();
|
||||
|
||||
@Override
|
||||
protected Set<Class<? extends StateDescriptor>> getStateDescriptors() {
|
||||
return Collections.singleton(Bind2Module.Bind2StateDescriptor.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bind2Module constructXmppConnectionModule(
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
return new Bind2Module(this, connectionInternal);
|
||||
}
|
||||
|
||||
public static class Builder extends ModularXmppClientToServerConnectionModuleDescriptor.Builder {
|
||||
|
||||
protected Builder(ModularXmppClientToServerConnectionConfiguration.Builder connectionConfigurationBuilder) {
|
||||
super(connectionConfigurationBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bind2ModuleDescriptor build() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2020 Florian Schmaus
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes and interfaces for Bind 2.0 (XEP-0386).
|
||||
*/
|
||||
package org.jivesoftware.smack.bind2;
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,167 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2019-2020 Florian Schmaus
|
||||
*
|
||||
* 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.c2s;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jivesoftware.smack.ConnectionConfiguration;
|
||||
import org.jivesoftware.smack.SmackConfiguration;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptor;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptorGraph;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptorGraph.GraphVertex;
|
||||
import org.jivesoftware.smack.util.CollectionUtil;
|
||||
|
||||
public final class ModularXmppClientToServerConnectionConfiguration extends ConnectionConfiguration {
|
||||
|
||||
final Set<ModularXmppClientToServerConnectionModuleDescriptor> moduleDescriptors;
|
||||
|
||||
final GraphVertex<StateDescriptor> initialStateDescriptorVertex;
|
||||
|
||||
private ModularXmppClientToServerConnectionConfiguration(Builder builder) {
|
||||
super(builder);
|
||||
|
||||
moduleDescriptors = Collections.unmodifiableSet(CollectionUtil.newSetWith(builder.modulesDescriptors.values()));
|
||||
|
||||
Set<Class<? extends StateDescriptor>> backwardEdgeStateDescriptors = new HashSet<>();
|
||||
// Add backward edges from configured connection modules. Note that all state descriptors from module
|
||||
// descriptors are backwards edges.
|
||||
for (ModularXmppClientToServerConnectionModuleDescriptor moduleDescriptor : moduleDescriptors) {
|
||||
Set<Class<? extends StateDescriptor>> moduleStateDescriptors = moduleDescriptor.getStateDescriptors();
|
||||
backwardEdgeStateDescriptors.addAll(moduleStateDescriptors);
|
||||
}
|
||||
|
||||
try {
|
||||
initialStateDescriptorVertex = StateDescriptorGraph.constructStateDescriptorGraph(backwardEdgeStateDescriptors);
|
||||
}
|
||||
catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
||||
| NoSuchMethodException | SecurityException e) {
|
||||
// TODO: Depending on the exact exception thrown, this potentially indicates an invalid connection
|
||||
// configuration, e.g. there is no edge from disconnected to connected.
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void printStateGraphInDotFormat(PrintWriter pw, boolean breakStateName) {
|
||||
StateDescriptorGraph.stateDescriptorGraphToDot(Collections.singleton(initialStateDescriptorVertex), pw,
|
||||
breakStateName);
|
||||
}
|
||||
|
||||
public String getStateGraphInDotFormat() {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
|
||||
printStateGraphInDotFormat(pw, true);
|
||||
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static final class Builder
|
||||
extends ConnectionConfiguration.Builder<Builder, ModularXmppClientToServerConnectionConfiguration> {
|
||||
|
||||
private final Map<Class<? extends ModularXmppClientToServerConnectionModuleDescriptor>, ModularXmppClientToServerConnectionModuleDescriptor> modulesDescriptors = new HashMap<>();
|
||||
|
||||
private Builder() {
|
||||
SmackConfiguration.addAllKnownModulesTo(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModularXmppClientToServerConnectionConfiguration build() {
|
||||
return new ModularXmppClientToServerConnectionConfiguration(this);
|
||||
}
|
||||
|
||||
void addModule(ModularXmppClientToServerConnectionModuleDescriptor connectionModule) {
|
||||
Class<? extends ModularXmppClientToServerConnectionModuleDescriptor> moduleDescriptorClass = connectionModule.getClass();
|
||||
if (modulesDescriptors.containsKey(moduleDescriptorClass)) {
|
||||
throw new IllegalArgumentException("A connection module for " + moduleDescriptorClass + " is already configured");
|
||||
}
|
||||
modulesDescriptors.put(moduleDescriptorClass, connectionModule);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Builder addModule(Class<? extends ModularXmppClientToServerConnectionModuleDescriptor> moduleClass) {
|
||||
Class<?>[] declaredClasses = moduleClass.getDeclaredClasses();
|
||||
|
||||
Class<? extends ModularXmppClientToServerConnectionModuleDescriptor.Builder> builderClass = null;
|
||||
for (Class<?> declaredClass : declaredClasses) {
|
||||
if (!ModularXmppClientToServerConnectionModuleDescriptor.Builder.class.isAssignableFrom(declaredClass)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
builderClass = (Class<? extends ModularXmppClientToServerConnectionModuleDescriptor.Builder>) declaredClass;
|
||||
break;
|
||||
}
|
||||
|
||||
if (builderClass == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Found no builder for " + moduleClass + ". Delcared classes: " + Arrays.toString(declaredClasses));
|
||||
}
|
||||
|
||||
return with(builderClass).buildModule();
|
||||
}
|
||||
|
||||
public <B extends ModularXmppClientToServerConnectionModuleDescriptor.Builder> B with(
|
||||
Class<? extends B> moduleDescriptorBuilderClass) {
|
||||
Constructor<? extends B> moduleDescriptorBuilderCosntructor;
|
||||
try {
|
||||
moduleDescriptorBuilderCosntructor = moduleDescriptorBuilderClass.getDeclaredConstructor(
|
||||
ModularXmppClientToServerConnectionConfiguration.Builder.class);
|
||||
} catch (NoSuchMethodException | SecurityException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
|
||||
moduleDescriptorBuilderCosntructor.setAccessible(true);
|
||||
|
||||
B moduleDescriptorBuilder;
|
||||
try {
|
||||
moduleDescriptorBuilder = moduleDescriptorBuilderCosntructor.newInstance(this);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
|
||||
| InvocationTargetException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
|
||||
return moduleDescriptorBuilder;
|
||||
}
|
||||
|
||||
public Builder removeModule(Class<? extends ModularXmppClientToServerConnectionModuleDescriptor> moduleClass) {
|
||||
modulesDescriptors.remove(moduleClass);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public Builder removeAllModules() {
|
||||
modulesDescriptors.clear();
|
||||
return getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Builder getThis() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2020 Florian Schmaus
|
||||
*
|
||||
* 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.c2s;
|
||||
|
||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||
|
||||
public abstract class ModularXmppClientToServerConnectionModule<MD extends ModularXmppClientToServerConnectionModuleDescriptor> {
|
||||
|
||||
protected final MD moduleDescriptor;
|
||||
|
||||
protected final ModularXmppClientToServerConnectionInternal connectionInternal;
|
||||
|
||||
protected ModularXmppClientToServerConnectionModule(MD moduleDescriptor,
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
this.moduleDescriptor = moduleDescriptor;
|
||||
this.connectionInternal = connectionInternal;
|
||||
}
|
||||
|
||||
public MD getModuleDescriptor() {
|
||||
return moduleDescriptor;
|
||||
}
|
||||
|
||||
protected XmppClientToServerTransport getTransport() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2019-2020 Florian Schmaus
|
||||
*
|
||||
* 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.c2s;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptor;
|
||||
|
||||
public abstract class ModularXmppClientToServerConnectionModuleDescriptor {
|
||||
|
||||
protected abstract Set<Class<? extends StateDescriptor>> getStateDescriptors();
|
||||
|
||||
protected abstract ModularXmppClientToServerConnectionModule<? extends ModularXmppClientToServerConnectionModuleDescriptor> constructXmppConnectionModule(
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal);
|
||||
|
||||
public abstract static class Builder {
|
||||
private final ModularXmppClientToServerConnectionConfiguration.Builder connectionConfigurationBuilder;
|
||||
|
||||
protected Builder(ModularXmppClientToServerConnectionConfiguration.Builder connectionConfigurationBuilder) {
|
||||
this.connectionConfigurationBuilder = connectionConfigurationBuilder;
|
||||
}
|
||||
|
||||
protected abstract ModularXmppClientToServerConnectionModuleDescriptor build();
|
||||
|
||||
public ModularXmppClientToServerConnectionConfiguration.Builder buildModule() {
|
||||
ModularXmppClientToServerConnectionModuleDescriptor moduleDescriptor = build();
|
||||
connectionConfigurationBuilder.addModule(moduleDescriptor);
|
||||
return connectionConfigurationBuilder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2019-2020 Florian Schmaus
|
||||
*
|
||||
* 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.c2s;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
import org.jivesoftware.smack.SmackFuture;
|
||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||
|
||||
public abstract class XmppClientToServerTransport {
|
||||
|
||||
protected final ModularXmppClientToServerConnectionInternal connectionInternal;
|
||||
|
||||
protected XmppClientToServerTransport(ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
this.connectionInternal = connectionInternal;
|
||||
}
|
||||
|
||||
protected abstract void resetDiscoveredConnectionEndpoints();
|
||||
|
||||
protected abstract List<SmackFuture<LookupConnectionEndpointsResult, Exception>> lookupConnectionEndpoints();
|
||||
|
||||
protected abstract void loadConnectionEndpoints(LookupConnectionEndpointsSuccess lookupConnectionEndpointsSuccess);
|
||||
|
||||
/**
|
||||
* Notify the transport that new outgoing data is available. Usually this method does not need to be called
|
||||
* explicitly, only if the filters are modified so that they potentially produced new data.
|
||||
*/
|
||||
protected abstract void afterFiltersClosed();
|
||||
|
||||
/**
|
||||
* Called by the CloseConnection state.
|
||||
*/
|
||||
protected abstract void disconnect();
|
||||
|
||||
protected abstract void notifyAboutNewOutgoingElements();
|
||||
|
||||
public abstract SSLSession getSslSession();
|
||||
|
||||
public abstract boolean isConnected();
|
||||
|
||||
public boolean isTransportSecured() {
|
||||
return getSslSession() != null;
|
||||
}
|
||||
|
||||
public abstract Stats getStats();
|
||||
|
||||
public abstract static class Stats {
|
||||
}
|
||||
|
||||
protected interface LookupConnectionEndpointsResult {
|
||||
}
|
||||
|
||||
protected interface LookupConnectionEndpointsSuccess extends LookupConnectionEndpointsResult {
|
||||
}
|
||||
|
||||
public interface LookupConnectionEndpointsFailed extends LookupConnectionEndpointsResult {
|
||||
// TODO: Add something like getExceptions() or getConnectionExceptions()?
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2020 Florian Schmaus
|
||||
*
|
||||
* 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.c2s.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Queue;
|
||||
|
||||
import org.jivesoftware.smack.AbstractXMPPConnection.SmackTlsContext;
|
||||
import org.jivesoftware.smack.SmackException.ConnectionUnexpectedTerminatedException;
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackReactor;
|
||||
import org.jivesoftware.smack.SmackReactor.ChannelSelectedCallback;
|
||||
import org.jivesoftware.smack.XMPPException.FailedNonzaException;
|
||||
import org.jivesoftware.smack.XmppInputOutputFilter;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection;
|
||||
import org.jivesoftware.smack.c2s.XmppClientToServerTransport;
|
||||
import org.jivesoftware.smack.debugger.SmackDebugger;
|
||||
import org.jivesoftware.smack.fsm.ConnectionStateEvent;
|
||||
import org.jivesoftware.smack.packet.Nonza;
|
||||
import org.jivesoftware.smack.packet.TopLevelStreamElement;
|
||||
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||
import org.jivesoftware.smack.util.Consumer;
|
||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||
|
||||
public abstract class ModularXmppClientToServerConnectionInternal {
|
||||
|
||||
private final SmackReactor reactor;
|
||||
|
||||
public final ModularXmppClientToServerConnection connection;
|
||||
|
||||
public final SmackDebugger smackDebugger;
|
||||
|
||||
public final Queue<TopLevelStreamElement> outgoingElementsQueue;
|
||||
|
||||
public ModularXmppClientToServerConnectionInternal(ModularXmppClientToServerConnection connection, SmackReactor reactor,
|
||||
SmackDebugger smackDebugger, Queue<TopLevelStreamElement> outgoingElementsQueue) {
|
||||
this.connection = connection;
|
||||
this.reactor = reactor;
|
||||
this.smackDebugger = smackDebugger;
|
||||
this.outgoingElementsQueue = outgoingElementsQueue;
|
||||
}
|
||||
|
||||
public SelectionKey registerWithSelector(SelectableChannel channel, int ops, ChannelSelectedCallback callback)
|
||||
throws ClosedChannelException {
|
||||
return reactor.registerWithSelector(channel, ops, callback);
|
||||
}
|
||||
|
||||
public void setInterestOps(SelectionKey selectionKey, int interestOps) {
|
||||
reactor.setInterestOps(selectionKey, interestOps);
|
||||
}
|
||||
|
||||
public final void withSmackDebugger(Consumer<SmackDebugger> smackDebuggerConsumer) {
|
||||
if (smackDebugger == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
smackDebuggerConsumer.accept(smackDebugger);
|
||||
}
|
||||
|
||||
public abstract XmlEnvironment getOutgoingStreamXmlEnvironment();
|
||||
|
||||
// TODO: The incomingElement parameter was previously of type TopLevelStreamElement, but I believe it has to be
|
||||
// of type string. But would this also work for BOSH or WebSocket?
|
||||
public abstract void parseAndProcessElement(String wrappedCompleteIncomingElement);
|
||||
|
||||
public abstract void notifyConnectionError(Exception e);
|
||||
|
||||
public abstract void onStreamOpen(XmlPullParser parser);
|
||||
|
||||
public abstract void onStreamClosed();
|
||||
|
||||
public abstract void fireFirstLevelElementSendListeners(TopLevelStreamElement element);
|
||||
|
||||
public abstract void invokeConnectionStateMachineListener(ConnectionStateEvent connectionStateEvent);
|
||||
|
||||
public abstract void addXmppInputOutputFilter(XmppInputOutputFilter xmppInputOutputFilter);
|
||||
|
||||
public abstract ListIterator<XmppInputOutputFilter> getXmppInputOutputFilterBeginIterator();
|
||||
|
||||
public abstract ListIterator<XmppInputOutputFilter> getXmppInputOutputFilterEndIterator();
|
||||
|
||||
public abstract void newStreamOpenWaitForFeaturesSequence(String waitFor) throws InterruptedException,
|
||||
ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException;
|
||||
|
||||
public abstract SmackTlsContext getSmackTlsContext()
|
||||
throws KeyManagementException, NoSuchAlgorithmException, CertificateException, IOException,
|
||||
UnrecoverableKeyException, KeyStoreException, NoSuchProviderException;
|
||||
|
||||
public abstract <SN extends Nonza, FN extends Nonza> SN sendAndWaitForResponse(Nonza nonza,
|
||||
Class<SN> successNonzaClass, Class<FN> failedNonzaClass)
|
||||
throws NoResponseException, NotConnectedException, FailedNonzaException, InterruptedException;
|
||||
|
||||
public abstract void asyncGo(Runnable runnable);
|
||||
|
||||
public abstract Exception getCurrentConnectionException();
|
||||
|
||||
public abstract void setCompressionEnabled(boolean compressionEnabled);
|
||||
|
||||
public abstract void setTransport(XmppClientToServerTransport xmppTransport);
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2020 Florian Schmaus
|
||||
*
|
||||
* 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.c2s.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.AuthenticatedAndResourceBoundStateDescriptor;
|
||||
import org.jivesoftware.smack.fsm.LoginContext;
|
||||
import org.jivesoftware.smack.fsm.State;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptor;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptorGraph.GraphVertex;
|
||||
import org.jivesoftware.smack.fsm.StateTransitionResult;
|
||||
import org.jivesoftware.smack.util.CollectionUtil;
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
|
||||
import org.jxmpp.jid.parts.Resourcepart;
|
||||
|
||||
public final class WalkStateGraphContext {
|
||||
private final Class<? extends StateDescriptor> initialStateClass;
|
||||
private final Class<? extends StateDescriptor> finalStateClass;
|
||||
private final Class<? extends StateDescriptor> mandatoryIntermediateState;
|
||||
private final LoginContext loginContext;
|
||||
|
||||
private final List<State> walkedStateGraphPath = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* A linked Map of failed States with their reason as value.
|
||||
*/
|
||||
final Map<State, StateTransitionResult> failedStates = new LinkedHashMap<>();
|
||||
|
||||
boolean mandatoryIntermediateStateHandled;
|
||||
|
||||
WalkStateGraphContext(Builder builder) {
|
||||
initialStateClass = builder.initialStateClass;
|
||||
finalStateClass = builder.finalStateClass;
|
||||
mandatoryIntermediateState = builder.mandatoryIntermediateState;
|
||||
loginContext = builder.loginContext;
|
||||
}
|
||||
|
||||
public void recordWalkTo(State state) {
|
||||
walkedStateGraphPath.add(state);
|
||||
}
|
||||
|
||||
public boolean isWalksFinalState(StateDescriptor stateDescriptor) {
|
||||
return stateDescriptor.getClass() == finalStateClass;
|
||||
}
|
||||
|
||||
public boolean isFinalStateAuthenticatedAndResourceBound() {
|
||||
return finalStateClass == AuthenticatedAndResourceBoundStateDescriptor.class;
|
||||
}
|
||||
|
||||
public GraphVertex<State> maybeReturnMandatoryImmediateState(List<GraphVertex<State>> outgoingStateEdges) {
|
||||
for (GraphVertex<State> outgoingStateVertex : outgoingStateEdges) {
|
||||
if (outgoingStateVertex.getElement().getStateDescriptor().getClass() == mandatoryIntermediateState) {
|
||||
mandatoryIntermediateStateHandled = true;
|
||||
return outgoingStateVertex;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<State> getWalk() {
|
||||
return CollectionUtil.newListWith(walkedStateGraphPath);
|
||||
}
|
||||
|
||||
public int getWalkLength() {
|
||||
return walkedStateGraphPath.size();
|
||||
}
|
||||
|
||||
public void appendWalkTo(List<State> walk) {
|
||||
walk.addAll(walkedStateGraphPath);
|
||||
}
|
||||
|
||||
public LoginContext getLoginContext() {
|
||||
return loginContext;
|
||||
}
|
||||
|
||||
public boolean stateAlreadyVisited(State state) {
|
||||
return walkedStateGraphPath.contains(state);
|
||||
}
|
||||
|
||||
public void recordFailedState(State state, StateTransitionResult stateTransitionResult) {
|
||||
failedStates.put(state, stateTransitionResult);
|
||||
}
|
||||
|
||||
public Map<State, StateTransitionResult> getFailedStates() {
|
||||
return new HashMap<>(failedStates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the way to the final state via the given successor state that would loop, i.e., lead over the initial state and
|
||||
* thus from a cycle.
|
||||
*
|
||||
* @param successorStateVertex the successor state to use on the way.
|
||||
* @return <code>true</code> if it would loop, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean wouldCauseCycle(GraphVertex<State> successorStateVertex) {
|
||||
Set<Class<? extends StateDescriptor>> visited = new HashSet<>();
|
||||
return wouldCycleRecursive(successorStateVertex, visited);
|
||||
}
|
||||
|
||||
private boolean wouldCycleRecursive(GraphVertex<State> stateVertex, Set<Class<? extends StateDescriptor>> visited) {
|
||||
Class<? extends StateDescriptor> stateVertexClass = stateVertex.getElement().getStateDescriptor().getClass();
|
||||
|
||||
if (stateVertexClass == initialStateClass) {
|
||||
return true;
|
||||
}
|
||||
if (finalStateClass == stateVertexClass || visited.contains(stateVertexClass)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
visited.add(stateVertexClass);
|
||||
|
||||
for (GraphVertex<State> successorStateVertex : stateVertex.getOutgoingEdges()) {
|
||||
boolean cycle = wouldCycleRecursive(successorStateVertex, visited);
|
||||
if (cycle) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Builder builder(Class<? extends StateDescriptor> initialStateClass, Class<? extends StateDescriptor> finalStateClass) {
|
||||
return new Builder(initialStateClass, finalStateClass);
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private final Class<? extends StateDescriptor> initialStateClass;
|
||||
private final Class<? extends StateDescriptor> finalStateClass;
|
||||
private Class<? extends StateDescriptor> mandatoryIntermediateState;
|
||||
private LoginContext loginContext;
|
||||
|
||||
private Builder(Class<? extends StateDescriptor> initialStateClass, Class<? extends StateDescriptor> finalStateClass) {
|
||||
this.initialStateClass = Objects.requireNonNull(initialStateClass);
|
||||
this.finalStateClass = Objects.requireNonNull(finalStateClass);
|
||||
}
|
||||
|
||||
public Builder withMandatoryIntermediateState(Class<? extends StateDescriptor> mandatoryIntermedidateState) {
|
||||
this.mandatoryIntermediateState = mandatoryIntermedidateState;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withLoginContext(String username, String password, Resourcepart resource) {
|
||||
LoginContext loginContext = new LoginContext(username, password, resource);
|
||||
return withLoginContext(loginContext);
|
||||
}
|
||||
|
||||
public Builder withLoginContext(LoginContext loginContext) {
|
||||
this.loginContext = loginContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WalkStateGraphContext build() {
|
||||
return new WalkStateGraphContext(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2020 Florian Schmaus
|
||||
*
|
||||
* 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's internal API for client-to-server (c2s) connections.
|
||||
*/
|
||||
package org.jivesoftware.smack.c2s.internal;
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2020 Florian Schmaus
|
||||
*
|
||||
* 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's (new) API for client-to-server (c2s) connections.
|
||||
*/
|
||||
package org.jivesoftware.smack.c2s;
|
|
@ -0,0 +1,132 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2020 Florian Schmaus
|
||||
*
|
||||
* 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.compression;
|
||||
|
||||
import org.jivesoftware.smack.ConnectionConfiguration;
|
||||
import org.jivesoftware.smack.SmackException.ConnectionUnexpectedTerminatedException;
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.XMPPException.FailedNonzaException;
|
||||
import org.jivesoftware.smack.XmppInputOutputFilter;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.AuthenticatedButUnboundStateDescriptor;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.ResourceBindingStateDescriptor;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionModule;
|
||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||
import org.jivesoftware.smack.c2s.internal.WalkStateGraphContext;
|
||||
import org.jivesoftware.smack.compress.packet.Compress;
|
||||
import org.jivesoftware.smack.compress.packet.Compressed;
|
||||
import org.jivesoftware.smack.compress.packet.Failure;
|
||||
import org.jivesoftware.smack.fsm.State;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptor;
|
||||
import org.jivesoftware.smack.fsm.StateTransitionResult;
|
||||
|
||||
public class CompressionModule extends ModularXmppClientToServerConnectionModule<CompressionModuleDescriptor> {
|
||||
|
||||
protected CompressionModule(CompressionModuleDescriptor moduleDescriptor,
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
super(moduleDescriptor, connectionInternal);
|
||||
}
|
||||
|
||||
public static final class CompressionStateDescriptor extends StateDescriptor {
|
||||
private CompressionStateDescriptor() {
|
||||
super(CompressionModule.CompressionState.class, 138);
|
||||
addPredeccessor(AuthenticatedButUnboundStateDescriptor.class);
|
||||
addSuccessor(AuthenticatedButUnboundStateDescriptor.class);
|
||||
declarePrecedenceOver(ResourceBindingStateDescriptor.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompressionModule.CompressionState constructState(ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
CompressionModule compressionModule = connectionInternal.connection.getConnectionModuleFor(CompressionModuleDescriptor.class);
|
||||
return compressionModule.constructCompressionState(this, connectionInternal);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CompressionState extends State {
|
||||
private XmppCompressionFactory selectedCompressionFactory;
|
||||
private XmppInputOutputFilter usedXmppInputOutputCompressionFitler;
|
||||
|
||||
private CompressionState(StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
super(stateDescriptor, connectionInternal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateTransitionResult.TransitionImpossible isTransitionToPossible(
|
||||
WalkStateGraphContext walkStateGraphContext) {
|
||||
final ConnectionConfiguration config = connectionInternal.connection.getConfiguration();
|
||||
if (!config.isCompressionEnabled()) {
|
||||
return new StateTransitionResult.TransitionImpossibleReason("Stream compression disabled by connection configuration");
|
||||
}
|
||||
|
||||
Compress.Feature compressFeature = connectionInternal.connection.getFeature(Compress.Feature.ELEMENT, Compress.NAMESPACE);
|
||||
if (compressFeature == null) {
|
||||
return new StateTransitionResult.TransitionImpossibleReason("Stream compression not supported or enabled by service");
|
||||
}
|
||||
|
||||
selectedCompressionFactory = XmppCompressionManager.getBestFactory(compressFeature);
|
||||
if (selectedCompressionFactory == null) {
|
||||
return new StateTransitionResult.TransitionImpossibleReason(
|
||||
"No matching compression factory for " + compressFeature.getMethods());
|
||||
}
|
||||
|
||||
usedXmppInputOutputCompressionFitler = selectedCompressionFactory.fabricate(config);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext)
|
||||
throws NoResponseException, NotConnectedException, FailedNonzaException, InterruptedException,
|
||||
ConnectionUnexpectedTerminatedException {
|
||||
final String compressionMethod = selectedCompressionFactory.getCompressionMethod();
|
||||
connectionInternal.sendAndWaitForResponse(new Compress(compressionMethod), Compressed.class, Failure.class);
|
||||
|
||||
connectionInternal.addXmppInputOutputFilter(usedXmppInputOutputCompressionFitler);
|
||||
|
||||
connectionInternal.newStreamOpenWaitForFeaturesSequence("server stream features after compression enabled");
|
||||
|
||||
connectionInternal.setCompressionEnabled(true);
|
||||
|
||||
return new CompressionTransitionSuccessResult(compressionMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetState() {
|
||||
selectedCompressionFactory = null;
|
||||
usedXmppInputOutputCompressionFitler = null;
|
||||
connectionInternal.setCompressionEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class CompressionTransitionSuccessResult extends StateTransitionResult.Success {
|
||||
private final String compressionMethod;
|
||||
|
||||
private CompressionTransitionSuccessResult(String compressionMethod) {
|
||||
super(compressionMethod + " compression enabled");
|
||||
this.compressionMethod = compressionMethod;
|
||||
}
|
||||
|
||||
public String getCompressionMethod() {
|
||||
return compressionMethod;
|
||||
}
|
||||
}
|
||||
|
||||
public CompressionState constructCompressionState(CompressionStateDescriptor compressionStateDescriptor,
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
return new CompressionState(compressionStateDescriptor, connectionInternal);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2020 Florian Schmaus
|
||||
*
|
||||
* 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.compression;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionModuleDescriptor;
|
||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptor;
|
||||
|
||||
public class CompressionModuleDescriptor extends ModularXmppClientToServerConnectionModuleDescriptor {
|
||||
|
||||
private static final CompressionModuleDescriptor INSTANCE = new CompressionModuleDescriptor();
|
||||
|
||||
@Override
|
||||
protected Set<Class<? extends StateDescriptor>> getStateDescriptors() {
|
||||
return Collections.singleton(CompressionModule.CompressionStateDescriptor.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompressionModule constructXmppConnectionModule(
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
return new CompressionModule(this, connectionInternal);
|
||||
}
|
||||
|
||||
public static final class Builder extends ModularXmppClientToServerConnectionModuleDescriptor.Builder {
|
||||
|
||||
private Builder(ModularXmppClientToServerConnectionConfiguration.Builder connectionConfigurationBuilder) {
|
||||
super(connectionConfigurationBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModularXmppClientToServerConnectionModuleDescriptor build() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -230,6 +230,11 @@ public final class ZlibXmppCompressionFactory extends XmppCompressionFactory {
|
|||
public Stats getStats() {
|
||||
return new Stats(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilterName() {
|
||||
return "Compression (zlib)";
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Stats {
|
||||
|
|
|
@ -1,806 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2019 Florian Schmaus
|
||||
*
|
||||
* 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.fsm;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
import org.jivesoftware.smack.AbstractXMPPConnection;
|
||||
import org.jivesoftware.smack.ConnectionConfiguration;
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.SmackException.ConnectionUnexpectedTerminatedException;
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.XMPPException.FailedNonzaException;
|
||||
import org.jivesoftware.smack.XMPPException.StreamErrorException;
|
||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||
import org.jivesoftware.smack.XmppInputOutputFilter;
|
||||
import org.jivesoftware.smack.compress.packet.Compress;
|
||||
import org.jivesoftware.smack.compress.packet.Compressed;
|
||||
import org.jivesoftware.smack.compress.packet.Failure;
|
||||
import org.jivesoftware.smack.compression.XmppCompressionFactory;
|
||||
import org.jivesoftware.smack.compression.XmppCompressionManager;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptorGraph.GraphVertex;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jivesoftware.smack.packet.StreamError;
|
||||
import org.jivesoftware.smack.parsing.SmackParsingException;
|
||||
import org.jivesoftware.smack.sasl.SASLErrorException;
|
||||
import org.jivesoftware.smack.sasl.SASLMechanism;
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||
|
||||
import org.jxmpp.jid.parts.Resourcepart;
|
||||
|
||||
public abstract class AbstractXmppStateMachineConnection extends AbstractXMPPConnection {
|
||||
|
||||
private final List<ConnectionStateMachineListener> connectionStateMachineListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
private boolean featuresReceived;
|
||||
|
||||
protected boolean streamResumed;
|
||||
|
||||
private GraphVertex<State> currentStateVertex;
|
||||
|
||||
private List<State> walkFromDisconnectToAuthenticated;
|
||||
|
||||
private final List<XmppInputOutputFilter> inputOutputFilters = new CopyOnWriteArrayList<>();
|
||||
private List<XmppInputOutputFilter> previousInputOutputFilters;
|
||||
|
||||
protected AbstractXmppStateMachineConnection(ConnectionConfiguration configuration, GraphVertex<StateDescriptor> initialStateDescriptorVertex) {
|
||||
super(configuration);
|
||||
currentStateVertex = StateDescriptorGraph.convertToStateGraph(initialStateDescriptorVertex, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loginInternal(String username, String password, Resourcepart resource)
|
||||
throws XMPPException, SmackException, IOException, InterruptedException {
|
||||
WalkStateGraphContext walkStateGraphContext = buildNewWalkTo(AuthenticatedAndResourceBoundStateDescriptor.class)
|
||||
.withLoginContext(username, password, resource)
|
||||
.build();
|
||||
walkStateGraph(walkStateGraphContext);
|
||||
}
|
||||
|
||||
protected static WalkStateGraphContextBuilder buildNewWalkTo(Class<? extends StateDescriptor> finalStateClass) {
|
||||
return new WalkStateGraphContextBuilder(finalStateClass);
|
||||
}
|
||||
|
||||
protected static final class WalkStateGraphContext {
|
||||
private final Class<? extends StateDescriptor> finalStateClass;
|
||||
private final Class<? extends StateDescriptor> mandatoryIntermediateState;
|
||||
private final LoginContext loginContext;
|
||||
|
||||
private final List<State> walkedStateGraphPath = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* A linked Map of failed States with their reason as value.
|
||||
*/
|
||||
private final Map<State, TransitionReason> failedStates = new LinkedHashMap<>();
|
||||
|
||||
private boolean mandatoryIntermediateStateHandled;
|
||||
|
||||
private WalkStateGraphContext(Class<? extends StateDescriptor> finalStateClass, Class<? extends StateDescriptor> mandatoryIntermedidateState, LoginContext loginContext) {
|
||||
this.finalStateClass = Objects.requireNonNull(finalStateClass);
|
||||
this.mandatoryIntermediateState = mandatoryIntermedidateState;
|
||||
this.loginContext = loginContext;
|
||||
}
|
||||
|
||||
public boolean isFinalStateAuthenticatedAndResourceBound() {
|
||||
return finalStateClass == AuthenticatedAndResourceBoundStateDescriptor.class;
|
||||
}
|
||||
}
|
||||
|
||||
protected static final class WalkStateGraphContextBuilder {
|
||||
private final Class<? extends StateDescriptor> finalStateClass;
|
||||
private Class<? extends StateDescriptor> mandatoryIntermedidateState;
|
||||
private LoginContext loginContext;
|
||||
|
||||
private WalkStateGraphContextBuilder(Class<? extends StateDescriptor> finalStateClass) {
|
||||
this.finalStateClass = finalStateClass;
|
||||
}
|
||||
|
||||
public WalkStateGraphContextBuilder withMandatoryIntermediateState(Class<? extends StateDescriptor> mandatoryIntermedidateState) {
|
||||
this.mandatoryIntermedidateState = mandatoryIntermedidateState;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WalkStateGraphContextBuilder withLoginContext(String username, String password, Resourcepart resource) {
|
||||
LoginContext loginContext = new LoginContext(username, password, resource);
|
||||
return withLoginContext(loginContext);
|
||||
}
|
||||
|
||||
public WalkStateGraphContextBuilder withLoginContext(LoginContext loginContext) {
|
||||
this.loginContext = loginContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WalkStateGraphContext build() {
|
||||
return new WalkStateGraphContext(finalStateClass, mandatoryIntermedidateState, loginContext);
|
||||
}
|
||||
}
|
||||
|
||||
protected final void walkStateGraph(WalkStateGraphContext walkStateGraphContext) throws XMPPErrorException, SASLErrorException,
|
||||
FailedNonzaException, IOException, SmackException, InterruptedException {
|
||||
// Save a copy of the current state
|
||||
GraphVertex<State> previousStateVertex = currentStateVertex;
|
||||
try {
|
||||
walkStateGraphInternal(walkStateGraphContext);
|
||||
}
|
||||
catch (XMPPErrorException | SASLErrorException | FailedNonzaException | IOException | SmackException
|
||||
| InterruptedException e) {
|
||||
currentStateVertex = previousStateVertex;
|
||||
// Reset that state.
|
||||
State revertedState = currentStateVertex.getElement();
|
||||
invokeConnectionStateMachineListener(new ConnectionStateEvent.StateRevertBackwardsWalk(revertedState));
|
||||
revertedState.resetState();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void walkStateGraphInternal(WalkStateGraphContext walkStateGraphContext)
|
||||
throws XMPPErrorException, SASLErrorException, IOException, SmackException, InterruptedException, FailedNonzaException {
|
||||
// Save a copy of the current state
|
||||
final GraphVertex<State> initialStateVertex = currentStateVertex;
|
||||
final State initialState = initialStateVertex.getElement();
|
||||
final StateDescriptor initialStateDescriptor = initialState.getStateDescriptor();
|
||||
|
||||
walkStateGraphContext.walkedStateGraphPath.add(initialState);
|
||||
|
||||
if (initialStateDescriptor.getClass() == walkStateGraphContext.finalStateClass) {
|
||||
// If this is used as final state, then it should be marked as such.
|
||||
assert initialStateDescriptor.isFinalState();
|
||||
|
||||
// We reached the final state.
|
||||
invokeConnectionStateMachineListener(new ConnectionStateEvent.FinalStateReached(initialState));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
List<GraphVertex<State>> outgoingStateEdges = currentStateVertex.getOutgoingEdges();
|
||||
|
||||
// See if we need to handle mandatory intermediate states.
|
||||
if (walkStateGraphContext.mandatoryIntermediateState != null && !walkStateGraphContext.mandatoryIntermediateStateHandled) {
|
||||
// Check if outgoingStateEdges contains the mandatory intermediate state.
|
||||
GraphVertex<State> mandatoryIntermediateStateVertex = null;
|
||||
for (GraphVertex<State> outgoingStateVertex : outgoingStateEdges) {
|
||||
if (outgoingStateVertex.getElement().getStateDescriptor().getClass() == walkStateGraphContext.mandatoryIntermediateState) {
|
||||
mandatoryIntermediateStateVertex = outgoingStateVertex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mandatoryIntermediateStateVertex != null) {
|
||||
walkStateGraphContext.mandatoryIntermediateStateHandled = true;
|
||||
TransitionReason reason = attemptEnterState(mandatoryIntermediateStateVertex, walkStateGraphContext);
|
||||
if (reason instanceof TransitionSuccessResult) {
|
||||
walkStateGraph(walkStateGraphContext);
|
||||
return;
|
||||
}
|
||||
|
||||
// We could not enter a mandatory intermediate state. Throw here.
|
||||
throw new StateMachineException.SmackMandatoryStateFailedException(
|
||||
mandatoryIntermediateStateVertex.getElement(), reason);
|
||||
}
|
||||
}
|
||||
|
||||
for (Iterator<GraphVertex<State>> it = outgoingStateEdges.iterator(); it.hasNext();) {
|
||||
GraphVertex<State> successorStateVertex = it.next();
|
||||
State successorState = successorStateVertex.getElement();
|
||||
TransitionReason reason = attemptEnterState(successorStateVertex, walkStateGraphContext);
|
||||
if (reason instanceof TransitionSuccessResult) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If attemptEnterState did not throw and did not return a value of type TransitionSuccessResult, then we
|
||||
// just record this value and go on from there. Note that reason may be null, which is returned by
|
||||
// attemptEnterState in case the state was already successfully handled. If this is the case, then we don't
|
||||
// record it.
|
||||
if (reason != null) {
|
||||
walkStateGraphContext.failedStates.put(successorState, reason);
|
||||
}
|
||||
|
||||
if (!it.hasNext()) {
|
||||
throw new StateMachineException.SmackStateGraphDeadEndException(walkStateGraphContext.walkedStateGraphPath, walkStateGraphContext.failedStates);
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the state graph by recursion.
|
||||
walkStateGraph(walkStateGraphContext);
|
||||
}
|
||||
|
||||
private TransitionReason attemptEnterState(GraphVertex<State> successorStateVertex,
|
||||
WalkStateGraphContext walkStateGraphContext)
|
||||
throws SmackException, XMPPErrorException, SASLErrorException, IOException, InterruptedException, FailedNonzaException {
|
||||
final State successorState = successorStateVertex.getElement();
|
||||
final StateDescriptor successorStateDescriptor = successorState.getStateDescriptor();
|
||||
|
||||
if (!successorStateDescriptor.isMultiVisitState() && walkStateGraphContext.walkedStateGraphPath.contains(successorState)) {
|
||||
// This can happen if a state leads back to the state where it originated from. See for example the
|
||||
// 'Compression' state. We return 'null' here to signal that the state can safely be ignored.
|
||||
return null;
|
||||
}
|
||||
|
||||
if (successorStateDescriptor.isNotImplemented()) {
|
||||
TransitionImpossibleBecauseNotImplemented transtionImpossibleBecauseNotImplemented = new TransitionImpossibleBecauseNotImplemented(
|
||||
successorStateDescriptor);
|
||||
invokeConnectionStateMachineListener(new ConnectionStateEvent.TransitionNotPossible(successorState,
|
||||
transtionImpossibleBecauseNotImplemented));
|
||||
return transtionImpossibleBecauseNotImplemented;
|
||||
}
|
||||
|
||||
final TransitionIntoResult transitionIntoResult;
|
||||
try {
|
||||
TransitionImpossibleReason transitionImpossibleReason = successorState.isTransitionToPossible(walkStateGraphContext);
|
||||
if (transitionImpossibleReason != null) {
|
||||
invokeConnectionStateMachineListener(new ConnectionStateEvent.TransitionNotPossible(successorState,
|
||||
transitionImpossibleReason));
|
||||
return transitionImpossibleReason;
|
||||
}
|
||||
|
||||
invokeConnectionStateMachineListener(new ConnectionStateEvent.AboutToTransitionInto(successorState));
|
||||
transitionIntoResult = successorState.transitionInto(walkStateGraphContext);
|
||||
} catch (SmackException | XMPPErrorException | SASLErrorException | IOException | InterruptedException
|
||||
| FailedNonzaException e) {
|
||||
// TODO Document why this is required given that there is another call site of resetState().
|
||||
invokeConnectionStateMachineListener(new ConnectionStateEvent.StateRevertBackwardsWalk(successorState));
|
||||
successorState.resetState();
|
||||
throw e;
|
||||
}
|
||||
if (transitionIntoResult instanceof TransitionFailureResult) {
|
||||
TransitionFailureResult transitionFailureResult = (TransitionFailureResult) transitionIntoResult;
|
||||
invokeConnectionStateMachineListener(new ConnectionStateEvent.TransitionFailed(successorState, transitionFailureResult));
|
||||
return transitionIntoResult;
|
||||
}
|
||||
|
||||
// If transitionIntoResult is not an instance of TransitionFailureResult, then it has to be of type
|
||||
// TransitionSuccessResult.
|
||||
TransitionSuccessResult transitionSuccessResult = (TransitionSuccessResult) transitionIntoResult;
|
||||
|
||||
currentStateVertex = successorStateVertex;
|
||||
invokeConnectionStateMachineListener(new ConnectionStateEvent.SuccessfullyTransitionedInto(successorState,
|
||||
transitionSuccessResult));
|
||||
|
||||
return transitionSuccessResult;
|
||||
}
|
||||
|
||||
protected abstract SSLSession getSSLSession();
|
||||
|
||||
@Override
|
||||
protected void afterFeaturesReceived() {
|
||||
featuresReceived = true;
|
||||
synchronized (this) {
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
protected final void parseAndProcessElement(String element) throws XmlPullParserException, IOException,
|
||||
InterruptedException, StreamErrorException, SmackException, SmackParsingException {
|
||||
XmlPullParser parser = PacketParserUtils.getParserFor(element);
|
||||
|
||||
// Skip the enclosing stream open what is guaranteed to be there.
|
||||
parser.next();
|
||||
|
||||
XmlPullParser.Event event = parser.getEventType();
|
||||
outerloop: while (true) {
|
||||
switch (event) {
|
||||
case START_ELEMENT:
|
||||
final String name = parser.getName();
|
||||
// Note that we don't handle "stream" here as it's done in the splitter.
|
||||
switch (name) {
|
||||
case Message.ELEMENT:
|
||||
case IQ.IQ_ELEMENT:
|
||||
case Presence.ELEMENT:
|
||||
try {
|
||||
parseAndProcessStanza(parser);
|
||||
} finally {
|
||||
// TODO: Here would be the following stream management code.
|
||||
// clientHandledStanzasCount = SMUtils.incrementHeight(clientHandledStanzasCount);
|
||||
}
|
||||
break;
|
||||
case "error":
|
||||
StreamError streamError = PacketParserUtils.parseStreamError(parser, null);
|
||||
saslFeatureReceived.reportFailure(new StreamErrorException(streamError));
|
||||
throw new StreamErrorException(streamError);
|
||||
case "features":
|
||||
parseFeatures(parser);
|
||||
afterFeaturesReceived();
|
||||
break;
|
||||
default:
|
||||
parseAndProcessNonza(parser);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case END_DOCUMENT:
|
||||
break outerloop;
|
||||
default: // fall out
|
||||
}
|
||||
event = parser.next();
|
||||
}
|
||||
}
|
||||
|
||||
protected synchronized void prepareToWaitForFeaturesReceived() {
|
||||
featuresReceived = false;
|
||||
}
|
||||
|
||||
protected void waitForFeaturesReceived(String waitFor)
|
||||
throws InterruptedException, ConnectionUnexpectedTerminatedException, NoResponseException {
|
||||
long waitStartMs = System.currentTimeMillis();
|
||||
long timeoutMs = getReplyTimeout();
|
||||
synchronized (this) {
|
||||
while (!featuresReceived && currentConnectionException == null) {
|
||||
long remainingWaitMs = timeoutMs - (System.currentTimeMillis() - waitStartMs);
|
||||
if (remainingWaitMs <= 0) {
|
||||
throw NoResponseException.newWith(this, waitFor);
|
||||
}
|
||||
wait(remainingWaitMs);
|
||||
}
|
||||
if (currentConnectionException != null) {
|
||||
throw new SmackException.ConnectionUnexpectedTerminatedException(currentConnectionException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void newStreamOpenWaitForFeaturesSequence(String waitFor) throws InterruptedException,
|
||||
ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException {
|
||||
prepareToWaitForFeaturesReceived();
|
||||
sendStreamOpen();
|
||||
waitForFeaturesReceived(waitFor);
|
||||
}
|
||||
|
||||
protected final void addXmppInputOutputFilter(XmppInputOutputFilter xmppInputOutputFilter) {
|
||||
inputOutputFilters.add(0, xmppInputOutputFilter);
|
||||
}
|
||||
|
||||
protected final ListIterator<XmppInputOutputFilter> getXmppInputOutputFilterBeginIterator() {
|
||||
return inputOutputFilters.listIterator();
|
||||
}
|
||||
|
||||
protected final ListIterator<XmppInputOutputFilter> getXmppInputOutputFilterEndIterator() {
|
||||
return inputOutputFilters.listIterator(inputOutputFilters.size());
|
||||
}
|
||||
|
||||
protected final synchronized List<Object> getFilterStats() {
|
||||
Collection<XmppInputOutputFilter> filters;
|
||||
if (inputOutputFilters.isEmpty() && previousInputOutputFilters != null) {
|
||||
filters = previousInputOutputFilters;
|
||||
} else {
|
||||
filters = inputOutputFilters;
|
||||
}
|
||||
|
||||
List<Object> filterStats = new ArrayList<>(filters.size());
|
||||
for (XmppInputOutputFilter xmppInputOutputFilter : filters) {
|
||||
Object stats = xmppInputOutputFilter.getStats();
|
||||
if (stats != null) {
|
||||
filterStats.add(stats);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(filterStats);
|
||||
}
|
||||
|
||||
protected abstract class State {
|
||||
private final StateDescriptor stateDescriptor;
|
||||
|
||||
protected State(StateDescriptor stateDescriptor) {
|
||||
this.stateDescriptor = stateDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the state should be activated.
|
||||
*
|
||||
* @param walkStateGraphContext the context of the current state graph walk.
|
||||
* @return <code>null</code> if the state should be activated.
|
||||
* @throws SmackException in case a Smack exception occurs.
|
||||
*/
|
||||
protected TransitionImpossibleReason isTransitionToPossible(WalkStateGraphContext walkStateGraphContext) throws SmackException {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract TransitionIntoResult transitionInto(WalkStateGraphContext walkStateGraphContext)
|
||||
throws XMPPErrorException, SASLErrorException, IOException, SmackException, InterruptedException, FailedNonzaException;
|
||||
|
||||
StateDescriptor getStateDescriptor() {
|
||||
return stateDescriptor;
|
||||
}
|
||||
|
||||
protected void resetState() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "State " + stateDescriptor + ' ' + AbstractXmppStateMachineConnection.this;
|
||||
}
|
||||
|
||||
protected final void ensureNotOnOurWayToAuthenticatedAndResourceBound(WalkStateGraphContext walkStateGraphContext) {
|
||||
if (walkStateGraphContext.isFinalStateAuthenticatedAndResourceBound()) {
|
||||
throw new IllegalStateException(
|
||||
"Smack should never attempt to reach the authenticated and resource bound state over " + this
|
||||
+ ". This is probably a programming error within Smack, please report it to the develoeprs.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class TransitionReason {
|
||||
public final String reason;
|
||||
private TransitionReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return reason;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class TransitionImpossibleReason extends TransitionReason {
|
||||
public TransitionImpossibleReason(String reason) {
|
||||
super(reason);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class TransitionImpossibleBecauseNotImplemented extends TransitionImpossibleReason {
|
||||
public TransitionImpossibleBecauseNotImplemented(StateDescriptor stateDescriptor) {
|
||||
super(stateDescriptor.getFullStateName(false) + " is not implemented (yet)");
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract static class TransitionIntoResult extends TransitionReason {
|
||||
public TransitionIntoResult(String reason) {
|
||||
super(reason);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TransitionSuccessResult extends TransitionIntoResult {
|
||||
|
||||
public static final TransitionSuccessResult EMPTY_INSTANCE = new TransitionSuccessResult();
|
||||
|
||||
private TransitionSuccessResult() {
|
||||
super("");
|
||||
}
|
||||
|
||||
public TransitionSuccessResult(String reason) {
|
||||
super(reason);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class TransitionFailureResult extends TransitionIntoResult {
|
||||
private TransitionFailureResult(String reason) {
|
||||
super(reason);
|
||||
}
|
||||
}
|
||||
|
||||
protected final class NoOpState extends State {
|
||||
|
||||
private NoOpState(StateDescriptor stateDescriptor) {
|
||||
super(stateDescriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TransitionImpossibleReason isTransitionToPossible(WalkStateGraphContext walkStateGraphContext) {
|
||||
// Transition into a NoOpState is always possible.
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TransitionIntoResult transitionInto(WalkStateGraphContext walkStateGraphContext) {
|
||||
// Transition into a NoOpState always succeeds.
|
||||
return TransitionSuccessResult.EMPTY_INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DisconnectedStateDescriptor extends StateDescriptor {
|
||||
protected DisconnectedStateDescriptor() {
|
||||
super(DisconnectedState.class, StateDescriptor.Property.finalState);
|
||||
}
|
||||
}
|
||||
|
||||
private final class DisconnectedState extends State {
|
||||
|
||||
private DisconnectedState(StateDescriptor stateDescriptor) {
|
||||
super(stateDescriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TransitionIntoResult transitionInto(WalkStateGraphContext walkStateGraphContext) {
|
||||
if (inputOutputFilters.isEmpty()) {
|
||||
previousInputOutputFilters = null;
|
||||
} else {
|
||||
previousInputOutputFilters = new ArrayList<>(inputOutputFilters.size());
|
||||
previousInputOutputFilters.addAll(inputOutputFilters);
|
||||
inputOutputFilters.clear();
|
||||
}
|
||||
|
||||
ListIterator<State> it = walkFromDisconnectToAuthenticated.listIterator(
|
||||
walkFromDisconnectToAuthenticated.size());
|
||||
while (it.hasPrevious()) {
|
||||
State stateToReset = it.previous();
|
||||
stateToReset.resetState();
|
||||
}
|
||||
walkFromDisconnectToAuthenticated = null;
|
||||
|
||||
return TransitionSuccessResult.EMPTY_INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
protected static final class ConnectedButUnauthenticatedStateDescriptor extends StateDescriptor {
|
||||
private ConnectedButUnauthenticatedStateDescriptor() {
|
||||
super(ConnectedButUnauthenticatedState.class, StateDescriptor.Property.finalState);
|
||||
addSuccessor(SaslAuthenticationStateDescriptor.class);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ConnectedButUnauthenticatedState extends State {
|
||||
private ConnectedButUnauthenticatedState(StateDescriptor stateDescriptor) {
|
||||
super(stateDescriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TransitionIntoResult transitionInto(WalkStateGraphContext walkStateGraphContext) {
|
||||
assert walkFromDisconnectToAuthenticated == null;
|
||||
if (getStateDescriptor().getClass() == walkStateGraphContext.finalStateClass) {
|
||||
// If this is the final state, then record the walk so far.
|
||||
walkFromDisconnectToAuthenticated = new ArrayList<>(walkStateGraphContext.walkedStateGraphPath);
|
||||
}
|
||||
|
||||
connected = true;
|
||||
return TransitionSuccessResult.EMPTY_INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetState() {
|
||||
connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected static final class SaslAuthenticationStateDescriptor extends StateDescriptor {
|
||||
private SaslAuthenticationStateDescriptor() {
|
||||
super(SaslAuthenticationState.class, "RFC 6120 § 6");
|
||||
addSuccessor(AuthenticatedButUnboundStateDescriptor.class);
|
||||
}
|
||||
}
|
||||
|
||||
private final class SaslAuthenticationState extends State {
|
||||
private SaslAuthenticationState(StateDescriptor stateDescriptor) {
|
||||
super(stateDescriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TransitionIntoResult transitionInto(WalkStateGraphContext walkStateGraphContext) throws XMPPErrorException,
|
||||
SASLErrorException, IOException, SmackException, InterruptedException {
|
||||
prepareToWaitForFeaturesReceived();
|
||||
|
||||
LoginContext loginContext = walkStateGraphContext.loginContext;
|
||||
SASLMechanism usedSaslMechanism = authenticate(loginContext.username, loginContext.password, config.getAuthzid(), getSSLSession());
|
||||
// authenticate() will only return if the SASL authentication was successful, but we also need to wait for the next round of stream features.
|
||||
|
||||
waitForFeaturesReceived("server stream features after SASL authentication");
|
||||
|
||||
return new SaslAuthenticationSuccessResult(usedSaslMechanism);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SaslAuthenticationSuccessResult extends TransitionSuccessResult {
|
||||
private final String saslMechanismName;
|
||||
|
||||
private SaslAuthenticationSuccessResult(SASLMechanism usedSaslMechanism) {
|
||||
super("SASL authentication successfull using " + usedSaslMechanism.getName());
|
||||
this.saslMechanismName = usedSaslMechanism.getName();
|
||||
}
|
||||
|
||||
public String getSaslMechanismName() {
|
||||
return saslMechanismName;
|
||||
}
|
||||
}
|
||||
|
||||
protected static final class AuthenticatedButUnboundStateDescriptor extends StateDescriptor {
|
||||
private AuthenticatedButUnboundStateDescriptor() {
|
||||
super(StateDescriptor.Property.multiVisitState);
|
||||
addSuccessor(ResourceBindingStateDescriptor.class);
|
||||
addSuccessor(CompressionStateDescriptor.class);
|
||||
}
|
||||
}
|
||||
|
||||
protected static final class ResourceBindingStateDescriptor extends StateDescriptor {
|
||||
private ResourceBindingStateDescriptor() {
|
||||
super(ResourceBindingState.class, "RFC 6120 § 7");
|
||||
addSuccessor(AuthenticatedAndResourceBoundStateDescriptor.class);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ResourceBindingState extends State {
|
||||
private ResourceBindingState(StateDescriptor stateDescriptor) {
|
||||
super(stateDescriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TransitionIntoResult transitionInto(WalkStateGraphContext walkStateGraphContext) throws XMPPErrorException,
|
||||
SASLErrorException, IOException, SmackException, InterruptedException {
|
||||
// TODO: The reportSuccess() is just a quick fix until there is a variant of the
|
||||
// bindResourceAndEstablishSession() method which does not require this.
|
||||
lastFeaturesReceived.reportSuccess();
|
||||
|
||||
LoginContext loginContext = walkStateGraphContext.loginContext;
|
||||
Resourcepart resource = bindResourceAndEstablishSession(loginContext.resource);
|
||||
streamResumed = false;
|
||||
|
||||
return new ResourceBoundResult(resource, loginContext.resource);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ResourceBoundResult extends TransitionSuccessResult {
|
||||
private final Resourcepart resource;
|
||||
|
||||
private ResourceBoundResult(Resourcepart boundResource, Resourcepart requestedResource) {
|
||||
super("Resource '" + boundResource + "' bound (requested: '" + requestedResource + "'");
|
||||
this.resource = boundResource;
|
||||
}
|
||||
|
||||
public Resourcepart getResource() {
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
|
||||
protected static final class CompressionStateDescriptor extends StateDescriptor {
|
||||
private CompressionStateDescriptor() {
|
||||
super(CompressionState.class, 138);
|
||||
addSuccessor(AuthenticatedButUnboundStateDescriptor.class);
|
||||
declarePrecedenceOver(ResourceBindingStateDescriptor.class);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean compressionEnabled;
|
||||
|
||||
private class CompressionState extends State {
|
||||
private XmppCompressionFactory selectedCompressionFactory;
|
||||
private XmppInputOutputFilter usedXmppInputOutputCompressionFitler;
|
||||
|
||||
protected CompressionState(StateDescriptor stateDescriptor) {
|
||||
super(stateDescriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TransitionImpossibleReason isTransitionToPossible(WalkStateGraphContext walkStateGraphContext) {
|
||||
if (!config.isCompressionEnabled()) {
|
||||
return new TransitionImpossibleReason("Stream compression disabled");
|
||||
}
|
||||
|
||||
Compress.Feature compressFeature = getFeature(Compress.Feature.ELEMENT, Compress.NAMESPACE);
|
||||
if (compressFeature == null) {
|
||||
return new TransitionImpossibleReason("Stream compression not supported");
|
||||
}
|
||||
|
||||
selectedCompressionFactory = XmppCompressionManager.getBestFactory(compressFeature);
|
||||
if (selectedCompressionFactory == null) {
|
||||
return new TransitionImpossibleReason("No matching compression factory");
|
||||
}
|
||||
|
||||
usedXmppInputOutputCompressionFitler = selectedCompressionFactory.fabricate(config);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TransitionIntoResult transitionInto(WalkStateGraphContext walkStateGraphContext)
|
||||
throws NoResponseException, NotConnectedException, FailedNonzaException, InterruptedException,
|
||||
ConnectionUnexpectedTerminatedException {
|
||||
final String compressionMethod = selectedCompressionFactory.getCompressionMethod();
|
||||
sendAndWaitForResponse(new Compress(compressionMethod), Compressed.class, Failure.class);
|
||||
|
||||
addXmppInputOutputFilter(usedXmppInputOutputCompressionFitler);
|
||||
|
||||
newStreamOpenWaitForFeaturesSequence("server stream features after compression enabled");
|
||||
|
||||
compressionEnabled = true;
|
||||
|
||||
return new CompressionTransitionSuccessResult(compressionMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetState() {
|
||||
selectedCompressionFactory = null;
|
||||
usedXmppInputOutputCompressionFitler = null;
|
||||
compressionEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class CompressionTransitionSuccessResult extends TransitionSuccessResult {
|
||||
private final String compressionMethod;
|
||||
|
||||
private CompressionTransitionSuccessResult(String compressionMethod) {
|
||||
super(compressionMethod + " compression enabled");
|
||||
this.compressionMethod = compressionMethod;
|
||||
}
|
||||
|
||||
public String getCompressionMethod() {
|
||||
return compressionMethod;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isUsingCompression() {
|
||||
return compressionEnabled;
|
||||
}
|
||||
|
||||
protected static final class AuthenticatedAndResourceBoundStateDescriptor extends StateDescriptor {
|
||||
private AuthenticatedAndResourceBoundStateDescriptor() {
|
||||
super(AuthenticatedAndResourceBoundState.class, StateDescriptor.Property.finalState);
|
||||
}
|
||||
}
|
||||
|
||||
private final class AuthenticatedAndResourceBoundState extends State {
|
||||
private AuthenticatedAndResourceBoundState(StateDescriptor stateDescriptor) {
|
||||
super(stateDescriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TransitionIntoResult transitionInto(WalkStateGraphContext walkStateGraphContext)
|
||||
throws NotConnectedException, InterruptedException {
|
||||
if (walkFromDisconnectToAuthenticated != null) {
|
||||
// If there was already a previous walk to ConnectedButUnauthenticated, then the context of the current
|
||||
// walk must not start from the 'Disconnected' state.
|
||||
assert walkStateGraphContext.walkedStateGraphPath.get(0).stateDescriptor.getClass() != DisconnectedStateDescriptor.class;
|
||||
walkFromDisconnectToAuthenticated.addAll(walkStateGraphContext.walkedStateGraphPath);
|
||||
} else {
|
||||
walkFromDisconnectToAuthenticated = new ArrayList<>(walkStateGraphContext.walkedStateGraphPath.size() + 1);
|
||||
walkFromDisconnectToAuthenticated.addAll(walkStateGraphContext.walkedStateGraphPath);
|
||||
}
|
||||
walkFromDisconnectToAuthenticated.add(this);
|
||||
|
||||
afterSuccessfulLogin(streamResumed);
|
||||
return TransitionSuccessResult.EMPTY_INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetState() {
|
||||
authenticated = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void addConnectionStateMachineListener(ConnectionStateMachineListener connectionStateMachineListener) {
|
||||
connectionStateMachineListeners.add(connectionStateMachineListener);
|
||||
}
|
||||
|
||||
public boolean removeConnectionStateMachineListener(ConnectionStateMachineListener connectionStateMachineListener) {
|
||||
return connectionStateMachineListeners.remove(connectionStateMachineListener);
|
||||
}
|
||||
|
||||
protected void invokeConnectionStateMachineListener(ConnectionStateEvent connectionStateEvent) {
|
||||
if (connectionStateMachineListeners.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASYNC_BUT_ORDERED.performAsyncButOrdered(this, () -> {
|
||||
for (ConnectionStateMachineListener connectionStateMachineListener : connectionStateMachineListeners) {
|
||||
connectionStateMachineListener.onConnectionStateEvent(connectionStateEvent, this);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Florian Schmaus
|
||||
* Copyright 2018-2020 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,29 +16,37 @@
|
|||
*/
|
||||
package org.jivesoftware.smack.fsm;
|
||||
|
||||
import org.jivesoftware.smack.fsm.AbstractXmppStateMachineConnection.State;
|
||||
import org.jivesoftware.smack.fsm.AbstractXmppStateMachineConnection.TransitionFailureResult;
|
||||
import org.jivesoftware.smack.fsm.AbstractXmppStateMachineConnection.TransitionImpossibleReason;
|
||||
import org.jivesoftware.smack.fsm.AbstractXmppStateMachineConnection.TransitionSuccessResult;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptorGraph.GraphVertex;
|
||||
|
||||
public class ConnectionStateEvent {
|
||||
|
||||
private final StateDescriptor stateDescriptor;
|
||||
private final StateDescriptor currentStateDescriptor;
|
||||
private final StateDescriptor successorStateDescriptor;
|
||||
|
||||
private final long timestamp;
|
||||
|
||||
protected ConnectionStateEvent(StateDescriptor stateDescriptor) {
|
||||
this.stateDescriptor = stateDescriptor;
|
||||
public ConnectionStateEvent(StateDescriptor currentStateDescriptor) {
|
||||
this(currentStateDescriptor, null);
|
||||
}
|
||||
|
||||
public ConnectionStateEvent(StateDescriptor currentStateDescriptor, StateDescriptor successorStateDescriptor) {
|
||||
this.currentStateDescriptor = currentStateDescriptor;
|
||||
this.successorStateDescriptor = successorStateDescriptor;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public StateDescriptor getStateDescriptor() {
|
||||
return stateDescriptor;
|
||||
return currentStateDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return stateDescriptor.getStateName() + ' ' + getClass().getSimpleName();
|
||||
if (successorStateDescriptor == null) {
|
||||
return getClass().getSimpleName() + ": " + currentStateDescriptor.getStateName();
|
||||
} else {
|
||||
return currentStateDescriptor.getStateName() + ' ' + getClass().getSimpleName() + ' '
|
||||
+ successorStateDescriptor.getStateName();
|
||||
}
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
|
@ -46,22 +54,22 @@ public class ConnectionStateEvent {
|
|||
}
|
||||
|
||||
public static class StateRevertBackwardsWalk extends ConnectionStateEvent {
|
||||
StateRevertBackwardsWalk(State state) {
|
||||
public StateRevertBackwardsWalk(State state) {
|
||||
super(state.getStateDescriptor());
|
||||
}
|
||||
}
|
||||
|
||||
public static class FinalStateReached extends ConnectionStateEvent {
|
||||
FinalStateReached(State state) {
|
||||
public FinalStateReached(State state) {
|
||||
super(state.getStateDescriptor());
|
||||
}
|
||||
}
|
||||
|
||||
public static class TransitionNotPossible extends ConnectionStateEvent {
|
||||
private final TransitionImpossibleReason transitionImpossibleReason;
|
||||
private final StateTransitionResult.TransitionImpossible transitionImpossibleReason;
|
||||
|
||||
TransitionNotPossible(State state, TransitionImpossibleReason reason) {
|
||||
super(state.getStateDescriptor());
|
||||
public TransitionNotPossible(State currentState, State successorState, StateTransitionResult.TransitionImpossible reason) {
|
||||
super(currentState.getStateDescriptor(), successorState.getStateDescriptor());
|
||||
this.transitionImpossibleReason = reason;
|
||||
}
|
||||
|
||||
|
@ -72,16 +80,16 @@ public class ConnectionStateEvent {
|
|||
}
|
||||
|
||||
public static class AboutToTransitionInto extends ConnectionStateEvent {
|
||||
AboutToTransitionInto(State state) {
|
||||
super(state.getStateDescriptor());
|
||||
public AboutToTransitionInto(State currentState, State successorState) {
|
||||
super(currentState.getStateDescriptor(), successorState.getStateDescriptor());
|
||||
}
|
||||
}
|
||||
|
||||
public static class TransitionFailed extends ConnectionStateEvent {
|
||||
private final TransitionFailureResult transitionFailedReason;
|
||||
private final StateTransitionResult.Failure transitionFailedReason;
|
||||
|
||||
TransitionFailed(State state, TransitionFailureResult transitionFailedReason) {
|
||||
super(state.getStateDescriptor());
|
||||
public TransitionFailed(State currentState, State failedSuccessorState, StateTransitionResult.Failure transitionFailedReason) {
|
||||
super(currentState.getStateDescriptor(), failedSuccessorState.getStateDescriptor());
|
||||
this.transitionFailedReason = transitionFailedReason;
|
||||
}
|
||||
|
||||
|
@ -91,10 +99,16 @@ public class ConnectionStateEvent {
|
|||
}
|
||||
}
|
||||
|
||||
public static class SuccessfullyTransitionedInto extends ConnectionStateEvent {
|
||||
private final TransitionSuccessResult transitionSuccessResult;
|
||||
public static class TransitionIgnoredDueCycle extends ConnectionStateEvent {
|
||||
public TransitionIgnoredDueCycle(GraphVertex<State> currentStateVertex, GraphVertex<State> successorStateVertexCausingCycle) {
|
||||
super(currentStateVertex.getElement().getStateDescriptor(), successorStateVertexCausingCycle.getElement().getStateDescriptor());
|
||||
}
|
||||
}
|
||||
|
||||
SuccessfullyTransitionedInto(State state, TransitionSuccessResult transitionSuccessResult) {
|
||||
public static class SuccessfullyTransitionedInto extends ConnectionStateEvent {
|
||||
private final StateTransitionResult.Success transitionSuccessResult;
|
||||
|
||||
public SuccessfullyTransitionedInto(State state, StateTransitionResult.Success transitionSuccessResult) {
|
||||
super(state.getStateDescriptor());
|
||||
this.transitionSuccessResult = transitionSuccessResult;
|
||||
}
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
*/
|
||||
package org.jivesoftware.smack.fsm;
|
||||
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection;
|
||||
|
||||
// TODO: Mark as java.lang.FunctionalInterface once Smack's minimum Android API level is 24 or higher.
|
||||
public interface ConnectionStateMachineListener {
|
||||
|
||||
void onConnectionStateEvent(ConnectionStateEvent connectionStateEvent, AbstractXmppStateMachineConnection connection);
|
||||
void onConnectionStateEvent(ConnectionStateEvent connectionStateEvent, ModularXmppClientToServerConnection connection);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Florian Schmaus
|
||||
* Copyright 2018-2020 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,11 +20,11 @@ import org.jxmpp.jid.parts.Resourcepart;
|
|||
|
||||
// TODO: At one point SASL authzid should be part of this.
|
||||
public class LoginContext {
|
||||
final String username;
|
||||
final String password;
|
||||
final Resourcepart resource;
|
||||
public final String username;
|
||||
public final String password;
|
||||
public final Resourcepart resource;
|
||||
|
||||
LoginContext(String username, String password, Resourcepart resource) {
|
||||
public LoginContext(String username, String password, Resourcepart resource) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.resource = resource;
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2020 Florian Schmaus
|
||||
*
|
||||
* 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.fsm;
|
||||
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection;
|
||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||
import org.jivesoftware.smack.c2s.internal.WalkStateGraphContext;
|
||||
|
||||
public class NoOpState extends State {
|
||||
|
||||
/**
|
||||
* Constructs a NoOpState. Note that the signature of this constructor is designed so that it mimics States which
|
||||
* are non-static inner classes of ModularXmppClientToServerConnection. That is why the first argument is not used.
|
||||
*
|
||||
* @param connection the connection.
|
||||
* @param stateDescriptor the related state descriptor
|
||||
* @param connectionInternal the internal connection API.
|
||||
*/
|
||||
@SuppressWarnings("UnusedVariable")
|
||||
protected NoOpState(ModularXmppClientToServerConnection connection, StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
super(stateDescriptor, connectionInternal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateTransitionResult.Success transitionInto(WalkStateGraphContext walkStateGraphContext) {
|
||||
// Transition into a NoOpState always succeeds.
|
||||
return StateTransitionResult.Success.EMPTY_INSTANCE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2020 Florian Schmaus
|
||||
*
|
||||
* 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.fsm;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPException.FailedNonzaException;
|
||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||
import org.jivesoftware.smack.c2s.internal.WalkStateGraphContext;
|
||||
import org.jivesoftware.smack.sasl.SASLErrorException;
|
||||
|
||||
/**
|
||||
* Note that this is an non-static inner class of XmppClientToServerConnection so that states can inspect and modify
|
||||
* the connection.
|
||||
*/
|
||||
public abstract class State {
|
||||
|
||||
protected final StateDescriptor stateDescriptor;
|
||||
|
||||
protected final ModularXmppClientToServerConnectionInternal connectionInternal;
|
||||
|
||||
protected State(StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
this.stateDescriptor = stateDescriptor;
|
||||
this.connectionInternal = connectionInternal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the state should be activated.
|
||||
*
|
||||
* @param walkStateGraphContext the context of the current state graph walk.
|
||||
* @return <code>null</code> if the state should be activated.
|
||||
* @throws SmackException in case a Smack exception occurs.
|
||||
*/
|
||||
public StateTransitionResult.TransitionImpossible isTransitionToPossible(WalkStateGraphContext walkStateGraphContext)
|
||||
throws SmackException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext)
|
||||
throws XMPPErrorException, SASLErrorException, IOException, SmackException,
|
||||
InterruptedException, FailedNonzaException;
|
||||
|
||||
public StateDescriptor getStateDescriptor() {
|
||||
return stateDescriptor;
|
||||
}
|
||||
|
||||
public void resetState() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "State " + stateDescriptor + ' ' + connectionInternal.connection;
|
||||
}
|
||||
|
||||
protected final void ensureNotOnOurWayToAuthenticatedAndResourceBound(
|
||||
WalkStateGraphContext walkStateGraphContext) {
|
||||
if (walkStateGraphContext.isFinalStateAuthenticatedAndResourceBound()) {
|
||||
throw new IllegalStateException(
|
||||
"Smack should never attempt to reach the authenticated and resource bound state over "
|
||||
+ this
|
||||
+ ". This is probably a programming error within Smack, please report it to the develoeprs.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Florian Schmaus
|
||||
* Copyright 2018-2020 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,9 +22,9 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.fsm.AbstractXmppStateMachineConnection.State;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection;
|
||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||
|
||||
public abstract class StateDescriptor {
|
||||
|
||||
|
@ -34,15 +34,13 @@ public abstract class StateDescriptor {
|
|||
notImplemented,
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(StateDescriptor.class.getName());
|
||||
|
||||
private final String stateName;
|
||||
private final int xepNum;
|
||||
private final String rfcSection;
|
||||
private final Set<Property> properties;
|
||||
|
||||
private final Class<? extends AbstractXmppStateMachineConnection.State> stateClass;
|
||||
private final Constructor<? extends AbstractXmppStateMachineConnection.State> stateClassConstructor;
|
||||
private final Class<? extends State> stateClass;
|
||||
private final Constructor<? extends State> stateClassConstructor;
|
||||
|
||||
private final Set<Class<? extends StateDescriptor>> successors = new HashSet<>();
|
||||
|
||||
|
@ -53,36 +51,36 @@ public abstract class StateDescriptor {
|
|||
private final Set<Class<? extends StateDescriptor>> inferiorTo = new HashSet<>();
|
||||
|
||||
protected StateDescriptor() {
|
||||
this(AbstractXmppStateMachineConnection.NoOpState.class, (Property) null);
|
||||
this(NoOpState.class, (Property) null);
|
||||
}
|
||||
|
||||
protected StateDescriptor(Property... properties) {
|
||||
this(AbstractXmppStateMachineConnection.NoOpState.class, properties);
|
||||
this(NoOpState.class, properties);
|
||||
}
|
||||
|
||||
protected StateDescriptor(Class<? extends AbstractXmppStateMachineConnection.State> stateClass) {
|
||||
protected StateDescriptor(Class<? extends State> stateClass) {
|
||||
this(stateClass, -1, null, Collections.emptySet());
|
||||
}
|
||||
|
||||
protected StateDescriptor(Class<? extends AbstractXmppStateMachineConnection.State> stateClass, Property... properties) {
|
||||
protected StateDescriptor(Class<? extends State> stateClass, Property... properties) {
|
||||
this(stateClass, -1, null, new HashSet<>(Arrays.asList(properties)));
|
||||
}
|
||||
|
||||
protected StateDescriptor(Class<? extends AbstractXmppStateMachineConnection.State> stateClass, int xepNum) {
|
||||
protected StateDescriptor(Class<? extends State> stateClass, int xepNum) {
|
||||
this(stateClass, xepNum, null, Collections.emptySet());
|
||||
}
|
||||
|
||||
protected StateDescriptor(Class<? extends AbstractXmppStateMachineConnection.State> stateClass, int xepNum,
|
||||
protected StateDescriptor(Class<? extends State> stateClass, int xepNum,
|
||||
Property... properties) {
|
||||
this(stateClass, xepNum, null, new HashSet<>(Arrays.asList(properties)));
|
||||
}
|
||||
|
||||
protected StateDescriptor(Class<? extends AbstractXmppStateMachineConnection.State> stateClass, String rfcSection) {
|
||||
protected StateDescriptor(Class<? extends State> stateClass, String rfcSection) {
|
||||
this(stateClass, -1, rfcSection, Collections.emptySet());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private StateDescriptor(Class<? extends AbstractXmppStateMachineConnection.State> stateClass, int xepNum,
|
||||
private StateDescriptor(Class<? extends State> stateClass, int xepNum,
|
||||
String rfcSection, Set<Property> properties) {
|
||||
this.stateClass = stateClass;
|
||||
if (rfcSection != null && xepNum > 0) {
|
||||
|
@ -92,26 +90,32 @@ public abstract class StateDescriptor {
|
|||
this.rfcSection = rfcSection;
|
||||
this.properties = properties;
|
||||
|
||||
Constructor<? extends AbstractXmppStateMachineConnection.State> selectedConstructor = null;
|
||||
Constructor<? extends State> selectedConstructor = null;
|
||||
Constructor<?>[] constructors = stateClass.getDeclaredConstructors();
|
||||
for (Constructor<?> constructor : constructors) {
|
||||
Class<?>[] parameterTypes = constructor.getParameterTypes();
|
||||
if (parameterTypes.length != 2) {
|
||||
LOGGER.warning("Invalid State class constructor: " + constructor);
|
||||
if (parameterTypes.length != 3) {
|
||||
continue;
|
||||
}
|
||||
if (!AbstractXmppStateMachineConnection.class.isAssignableFrom(parameterTypes[0])) {
|
||||
if (!ModularXmppClientToServerConnection.class.isAssignableFrom(parameterTypes[0])) {
|
||||
continue;
|
||||
}
|
||||
if (!StateDescriptor.class.isAssignableFrom(parameterTypes[1])) {
|
||||
continue;
|
||||
}
|
||||
if (!ModularXmppClientToServerConnectionInternal.class.isAssignableFrom(parameterTypes[2])) {
|
||||
continue;
|
||||
}
|
||||
selectedConstructor = (Constructor<? extends State>) constructor;
|
||||
break;
|
||||
}
|
||||
|
||||
if (selectedConstructor == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
stateClassConstructor = selectedConstructor;
|
||||
if (stateClassConstructor != null) {
|
||||
stateClassConstructor.setAccessible(true);
|
||||
} else {
|
||||
// TODO: Add validation check that if stateClassConstructor is 'null' the cosntructState() method is overriden.
|
||||
}
|
||||
|
||||
String className = getClass().getSimpleName();
|
||||
stateName = className.replaceFirst("StateDescriptor", "");
|
||||
|
@ -121,7 +125,7 @@ public abstract class StateDescriptor {
|
|||
addAndCheckNonExistent(successors, successor);
|
||||
}
|
||||
|
||||
protected void addPredeccessor(Class<? extends StateDescriptor> predeccessor) {
|
||||
public void addPredeccessor(Class<? extends StateDescriptor> predeccessor) {
|
||||
addAndCheckNonExistent(predecessors, predeccessor);
|
||||
}
|
||||
|
||||
|
@ -189,7 +193,7 @@ public abstract class StateDescriptor {
|
|||
return referenceCache;
|
||||
}
|
||||
|
||||
public Class<? extends AbstractXmppStateMachineConnection.State> getStateClass() {
|
||||
public Class<? extends State> getStateClass() {
|
||||
return stateClass;
|
||||
}
|
||||
|
||||
|
@ -205,9 +209,12 @@ public abstract class StateDescriptor {
|
|||
return properties.contains(Property.finalState);
|
||||
}
|
||||
|
||||
protected final AbstractXmppStateMachineConnection.State constructState(AbstractXmppStateMachineConnection connection) {
|
||||
protected State constructState(ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
ModularXmppClientToServerConnection connection = connectionInternal.connection;
|
||||
try {
|
||||
return stateClassConstructor.newInstance(connection, this);
|
||||
// If stateClassConstructor is null here, then you probably forgot to override the the
|
||||
// StateDescriptor.constructState() method?
|
||||
return stateClassConstructor.newInstance(connection, this, connectionInternal);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
|
||||
| InvocationTargetException e) {
|
||||
throw new IllegalStateException(e);
|
||||
|
|
|
@ -30,7 +30,8 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.fsm.AbstractXmppStateMachineConnection.DisconnectedStateDescriptor;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.DisconnectedStateDescriptor;
|
||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||
import org.jivesoftware.smack.util.Consumer;
|
||||
import org.jivesoftware.smack.util.MultiMap;
|
||||
|
||||
|
@ -134,7 +135,7 @@ public class StateDescriptorGraph {
|
|||
|
||||
// The preference graph is the graph where the precedence information of all successors is stored, which we will
|
||||
// topologically sort to find out which successor we should try first. It is a further new graph we use solely in
|
||||
// this step for every node. The graph is representent as map. There is no special marker for the initial node
|
||||
// this step for every node. The graph is represented as map. There is no special marker for the initial node
|
||||
// as it is not required for the topological sort performed later.
|
||||
Map<Class<? extends StateDescriptor>, GraphVertex<Class<? extends StateDescriptor>>> preferenceGraph = new HashMap<>(numSuccessors);
|
||||
|
||||
|
@ -171,7 +172,8 @@ public class StateDescriptorGraph {
|
|||
}
|
||||
}
|
||||
|
||||
// Perform a topological sort which returns the state descriptor classes in their priority.
|
||||
// Perform a topological sort which returns the state descriptor classes sorted by their priority. Highest
|
||||
// priority state descriptors first.
|
||||
List<GraphVertex<Class<? extends StateDescriptor>>> sortedSuccessors = topologicalSort(preferenceGraph.values());
|
||||
|
||||
// Handle the successor nodes which have not preference information available. Simply append them to the end of
|
||||
|
@ -222,19 +224,19 @@ public class StateDescriptorGraph {
|
|||
return initialNode;
|
||||
}
|
||||
|
||||
private static GraphVertex<AbstractXmppStateMachineConnection.State> convertToStateGraph(GraphVertex<StateDescriptor> stateDescriptorVertex,
|
||||
AbstractXmppStateMachineConnection connection, Map<StateDescriptor, GraphVertex<AbstractXmppStateMachineConnection.State>> handledStateDescriptors) {
|
||||
private static GraphVertex<State> convertToStateGraph(GraphVertex<StateDescriptor> stateDescriptorVertex,
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal, Map<StateDescriptor, GraphVertex<State>> handledStateDescriptors) {
|
||||
StateDescriptor stateDescriptor = stateDescriptorVertex.getElement();
|
||||
GraphVertex<AbstractXmppStateMachineConnection.State> stateVertex = handledStateDescriptors.get(stateDescriptor);
|
||||
GraphVertex<State> stateVertex = handledStateDescriptors.get(stateDescriptor);
|
||||
if (stateVertex != null) {
|
||||
return stateVertex;
|
||||
}
|
||||
|
||||
AbstractXmppStateMachineConnection.State state = stateDescriptor.constructState(connection);
|
||||
State state = stateDescriptor.constructState(connectionInternal);
|
||||
stateVertex = new GraphVertex<>(state);
|
||||
handledStateDescriptors.put(stateDescriptor, stateVertex);
|
||||
for (GraphVertex<StateDescriptor> successorStateDescriptorVertex : stateDescriptorVertex.getOutgoingEdges()) {
|
||||
GraphVertex<AbstractXmppStateMachineConnection.State> successorStateVertex = convertToStateGraph(successorStateDescriptorVertex, connection, handledStateDescriptors);
|
||||
GraphVertex<State> successorStateVertex = convertToStateGraph(successorStateDescriptorVertex, connectionInternal, handledStateDescriptors);
|
||||
// It is important that we keep the order of the edges. This should do it.
|
||||
stateVertex.addOutgoingEdge(successorStateVertex);
|
||||
}
|
||||
|
@ -242,10 +244,10 @@ public class StateDescriptorGraph {
|
|||
return stateVertex;
|
||||
}
|
||||
|
||||
static GraphVertex<AbstractXmppStateMachineConnection.State> convertToStateGraph(GraphVertex<StateDescriptor> initialStateDescriptor,
|
||||
AbstractXmppStateMachineConnection connection) {
|
||||
Map<StateDescriptor, GraphVertex<AbstractXmppStateMachineConnection.State>> handledStateDescriptors = new HashMap<>();
|
||||
GraphVertex<AbstractXmppStateMachineConnection.State> initialState = convertToStateGraph(initialStateDescriptor, connection,
|
||||
public static GraphVertex<State> convertToStateGraph(GraphVertex<StateDescriptor> initialStateDescriptor,
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
Map<StateDescriptor, GraphVertex<State>> handledStateDescriptors = new HashMap<>();
|
||||
GraphVertex<State> initialState = convertToStateGraph(initialStateDescriptor, connectionInternal,
|
||||
handledStateDescriptors);
|
||||
return initialState;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2019 Florian Schmaus
|
||||
* Copyright 2018-2020 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,18 +21,26 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.fsm.AbstractXmppStateMachineConnection.State;
|
||||
import org.jivesoftware.smack.fsm.AbstractXmppStateMachineConnection.TransitionReason;
|
||||
import org.jivesoftware.smack.c2s.internal.WalkStateGraphContext;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptorGraph.GraphVertex;
|
||||
|
||||
public abstract class StateMachineException extends SmackException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected StateMachineException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
protected StateMachineException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public static class SmackMandatoryStateFailedException extends StateMachineException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
SmackMandatoryStateFailedException(State state, TransitionReason failureReason) {
|
||||
public SmackMandatoryStateFailedException(State state, StateTransitionResult failureReason) {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,21 +48,36 @@ public abstract class StateMachineException extends SmackException {
|
|||
|
||||
private final List<State> walkedStateGraphPath;
|
||||
|
||||
private final Map<State, TransitionReason> failedStates;
|
||||
private final Map<State, StateTransitionResult> failedStates;
|
||||
|
||||
private final StateDescriptor deadEndState;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
SmackStateGraphDeadEndException(List<State> walkedStateGraphPath, Map<State, TransitionReason> failedStates) {
|
||||
this.walkedStateGraphPath = Collections.unmodifiableList(walkedStateGraphPath);
|
||||
this.failedStates = Collections.unmodifiableMap(failedStates);
|
||||
private SmackStateGraphDeadEndException(String message, WalkStateGraphContext walkStateGraphContext, GraphVertex<State> stateVertex) {
|
||||
super(message);
|
||||
this.walkedStateGraphPath = Collections.unmodifiableList(walkStateGraphContext.getWalk());
|
||||
this.failedStates = Collections.unmodifiableMap(walkStateGraphContext.getFailedStates());
|
||||
|
||||
deadEndState = stateVertex.getElement().getStateDescriptor();
|
||||
}
|
||||
|
||||
public List<State> getWalkedStateGraph() {
|
||||
return walkedStateGraphPath;
|
||||
}
|
||||
|
||||
public Map<State, TransitionReason> getFailedStates() {
|
||||
public Map<State, StateTransitionResult> getFailedStates() {
|
||||
return failedStates;
|
||||
}
|
||||
|
||||
public StateDescriptor getDeadEndState() {
|
||||
return deadEndState;
|
||||
}
|
||||
|
||||
public static SmackStateGraphDeadEndException from(WalkStateGraphContext walkStateGraphContext, GraphVertex<State> stateVertex) {
|
||||
String message = stateVertex + " has no successor vertexes";
|
||||
|
||||
return new SmackStateGraphDeadEndException(message, walkStateGraphContext, stateVertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2020 Florian Schmaus
|
||||
*
|
||||
* 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.fsm;
|
||||
|
||||
public abstract class StateTransitionResult {
|
||||
|
||||
private final String message;
|
||||
|
||||
protected StateTransitionResult(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public abstract static class AttemptResult extends StateTransitionResult {
|
||||
protected AttemptResult(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Success extends AttemptResult {
|
||||
|
||||
public static final Success EMPTY_INSTANCE = new Success();
|
||||
|
||||
private Success() {
|
||||
super("");
|
||||
}
|
||||
|
||||
public Success(String successMessage) {
|
||||
super(successMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Failure extends AttemptResult {
|
||||
public Failure(String failureMessage) {
|
||||
super(failureMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class FailureCausedByException<E extends Exception> extends Failure {
|
||||
private final E exception;
|
||||
|
||||
public FailureCausedByException(E exception) {
|
||||
super(exception.getMessage());
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public E getException() {
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class TransitionImpossible extends StateTransitionResult {
|
||||
protected TransitionImpossible(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TransitionImpossibleReason extends TransitionImpossible {
|
||||
public TransitionImpossibleReason(String reason) {
|
||||
super(reason);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TransitionImpossibleBecauseNotImplemented extends TransitionImpossibleReason {
|
||||
public TransitionImpossibleBecauseNotImplemented(StateDescriptor stateDescriptor) {
|
||||
super(stateDescriptor.getFullStateName(false) + " is not implemented (yet)");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2019-2020 Florian Schmaus
|
||||
*
|
||||
* 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.isr;
|
||||
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.AuthenticatedAndResourceBoundStateDescriptor;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.ConnectedButUnauthenticatedStateDescriptor;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.SaslAuthenticationStateDescriptor;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionModule;
|
||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||
import org.jivesoftware.smack.c2s.internal.WalkStateGraphContext;
|
||||
import org.jivesoftware.smack.fsm.State;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptor;
|
||||
import org.jivesoftware.smack.fsm.StateTransitionResult;
|
||||
|
||||
public class InstantStreamResumptionModule extends ModularXmppClientToServerConnectionModule<InstantStreamResumptionModuleDescriptor> {
|
||||
|
||||
protected InstantStreamResumptionModule(InstantStreamResumptionModuleDescriptor instantStreamResumptionModuleDescriptor,
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
super(instantStreamResumptionModuleDescriptor, connectionInternal);
|
||||
}
|
||||
|
||||
public static final class InstantStreamResumptionStateDescriptor extends StateDescriptor {
|
||||
private InstantStreamResumptionStateDescriptor() {
|
||||
super(InstantStreamResumptionState.class, 397, StateDescriptor.Property.notImplemented);
|
||||
|
||||
addSuccessor(AuthenticatedAndResourceBoundStateDescriptor.class);
|
||||
addPredeccessor(ConnectedButUnauthenticatedStateDescriptor.class);
|
||||
declarePrecedenceOver(SaslAuthenticationStateDescriptor.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InstantStreamResumptionModule.InstantStreamResumptionState constructState(ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
// This is the trick: the module is constructed prior the states, so we get the actual state out of the module by fetching the module from the connection.
|
||||
InstantStreamResumptionModule isrModule = connectionInternal.connection.getConnectionModuleFor(InstantStreamResumptionModuleDescriptor.class);
|
||||
return isrModule.constructInstantStreamResumptionState(this, connectionInternal);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean useIsr = true;
|
||||
|
||||
private final class InstantStreamResumptionState extends State {
|
||||
private InstantStreamResumptionState(InstantStreamResumptionStateDescriptor instantStreamResumptionStateDescriptor,
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
super(instantStreamResumptionStateDescriptor, connectionInternal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateTransitionResult.TransitionImpossible isTransitionToPossible(WalkStateGraphContext walkStateGraphContext) {
|
||||
if (!useIsr) {
|
||||
return new StateTransitionResult.TransitionImpossibleReason("Instant stream resumption not enabled nor implemented");
|
||||
}
|
||||
|
||||
return new StateTransitionResult.TransitionImpossibleBecauseNotImplemented(stateDescriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) {
|
||||
throw new IllegalStateException("Instant stream resumption not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
public void setInstantStreamResumptionEnabled(boolean useIsr) {
|
||||
this.useIsr = useIsr;
|
||||
}
|
||||
|
||||
public InstantStreamResumptionState constructInstantStreamResumptionState(
|
||||
InstantStreamResumptionStateDescriptor instantStreamResumptionStateDescriptor,
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
return new InstantStreamResumptionState(instantStreamResumptionStateDescriptor, connectionInternal);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2019-2020 Florian Schmaus
|
||||
*
|
||||
* 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.isr;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionModuleDescriptor;
|
||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||
import org.jivesoftware.smack.fsm.StateDescriptor;
|
||||
|
||||
public class InstantStreamResumptionModuleDescriptor extends ModularXmppClientToServerConnectionModuleDescriptor {
|
||||
|
||||
private static final InstantStreamResumptionModuleDescriptor INSTANCE = new InstantStreamResumptionModuleDescriptor();
|
||||
|
||||
@Override
|
||||
protected Set<Class<? extends StateDescriptor>> getStateDescriptors() {
|
||||
return Collections.singleton(InstantStreamResumptionModule.InstantStreamResumptionStateDescriptor.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InstantStreamResumptionModule constructXmppConnectionModule(
|
||||
ModularXmppClientToServerConnectionInternal connectionInternal) {
|
||||
return new InstantStreamResumptionModule(this, connectionInternal);
|
||||
}
|
||||
|
||||
public static final class Builder extends ModularXmppClientToServerConnectionModuleDescriptor.Builder {
|
||||
|
||||
private Builder(ModularXmppClientToServerConnectionConfiguration.Builder connectionConfigurationBuilder) {
|
||||
super(connectionConfigurationBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModularXmppClientToServerConnectionModuleDescriptor build() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2020 Florian Schmaus
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes and interfaces for Instant Stream Resumption (ISR) (XEP-0397).
|
||||
*
|
||||
* @see <a href="https://xmpp.org/extensions/xep-0397.html">XEP-0397: Instant Stream Resumption</a>
|
||||
*/
|
||||
package org.jivesoftware.smack.isr;
|
|
@ -163,15 +163,20 @@ public class ArrayBlockingQueueWithShutdown<E> extends AbstractQueue<E> implemen
|
|||
/**
|
||||
* Start the queue. Newly created instances will be started automatically, thus this only needs
|
||||
* to be called after {@link #shutdown()}.
|
||||
*
|
||||
* @return <code>true</code> if the queues was shutdown before, <code>false</code> if not.
|
||||
*/
|
||||
public void start() {
|
||||
public boolean start() {
|
||||
boolean previousIsShutdown;
|
||||
lock.lock();
|
||||
try {
|
||||
previousIsShutdown = isShutdown;
|
||||
isShutdown = false;
|
||||
}
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return previousIsShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2019 Florian Schmaus
|
||||
* Copyright 2015-2020 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,8 +18,10 @@ package org.jivesoftware.smack.util;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class CollectionUtil {
|
||||
|
||||
|
@ -56,4 +58,11 @@ public class CollectionUtil {
|
|||
}
|
||||
return new ArrayList<>(collection);
|
||||
}
|
||||
|
||||
public static <T> Set<T> newSetWith(Collection<? extends T> collection) {
|
||||
if (collection == null) {
|
||||
return null;
|
||||
}
|
||||
return new HashSet<>(collection);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2003-2005 Jive Software, 2016-2018 Florian Schmaus.
|
||||
* Copyright 2003-2005 Jive Software, 2016-2020 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,23 +16,9 @@
|
|||
*/
|
||||
package org.jivesoftware.smack.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode;
|
||||
import org.jivesoftware.smack.util.dns.DNSResolver;
|
||||
import org.jivesoftware.smack.util.dns.HostAddress;
|
||||
import org.jivesoftware.smack.util.dns.SRVRecord;
|
||||
import org.jivesoftware.smack.util.dns.SmackDaneProvider;
|
||||
|
||||
import org.minidns.dnsname.DnsName;
|
||||
|
||||
/**
|
||||
* Utility class to perform DNS lookups for XMPP services.
|
||||
*
|
||||
|
@ -41,10 +27,6 @@ import org.minidns.dnsname.DnsName;
|
|||
*/
|
||||
public class DNSUtil {
|
||||
|
||||
public static final String XMPP_CLIENT_DNS_SRV_PREFIX = "_xmpp-client._tcp";
|
||||
public static final String XMPP_SERVER_DNS_SRV_PREFIX = "_xmpp-server._tcp";
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(DNSUtil.class.getName());
|
||||
private static DNSResolver dnsResolver = null;
|
||||
private static SmackDaneProvider daneProvider;
|
||||
|
||||
|
@ -84,188 +66,4 @@ public class DNSUtil {
|
|||
return daneProvider;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ImmutableEnumChecker")
|
||||
enum DomainType {
|
||||
server(XMPP_SERVER_DNS_SRV_PREFIX),
|
||||
client(XMPP_CLIENT_DNS_SRV_PREFIX),
|
||||
;
|
||||
public final DnsName srvPrefix;
|
||||
|
||||
DomainType(String srvPrefixString) {
|
||||
srvPrefix = DnsName.from(srvPrefixString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of HostAddresses under which the specified XMPP server can be reached at for client-to-server
|
||||
* communication. A DNS lookup for a SRV record in the form "_xmpp-client._tcp.example.com" is attempted, according
|
||||
* to section 3.2.1 of RFC 6120. If that lookup fails, it's assumed that the XMPP server lives at the host resolved
|
||||
* by a DNS lookup at the specified domain on the default port of 5222.
|
||||
* <p>
|
||||
* As an example, a lookup for "example.com" may return "im.example.com:5269".
|
||||
* </p>
|
||||
*
|
||||
* @param domain the domain.
|
||||
* @param failedAddresses on optional list that will be populated with host addresses that failed to resolve.
|
||||
* @param dnssecMode DNSSec mode.
|
||||
* @return List of HostAddress, which encompasses the hostname and port that the
|
||||
* XMPP server can be reached at for the specified domain.
|
||||
*/
|
||||
public static List<HostAddress> resolveXMPPServiceDomain(DnsName domain, List<HostAddress> failedAddresses, DnssecMode dnssecMode) {
|
||||
return resolveDomain(domain, DomainType.client, failedAddresses, dnssecMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of HostAddresses under which the specified XMPP server can be reached at for server-to-server
|
||||
* communication. A DNS lookup for a SRV record in the form "_xmpp-server._tcp.example.com" is attempted, according
|
||||
* to section 3.2.1 of RFC 6120. If that lookup fails , it's assumed that the XMPP server lives at the host resolved
|
||||
* by a DNS lookup at the specified domain on the default port of 5269.
|
||||
* <p>
|
||||
* As an example, a lookup for "example.com" may return "im.example.com:5269".
|
||||
* </p>
|
||||
*
|
||||
* @param domain the domain.
|
||||
* @param failedAddresses on optional list that will be populated with host addresses that failed to resolve.
|
||||
* @param dnssecMode DNSSec mode.
|
||||
* @return List of HostAddress, which encompasses the hostname and port that the
|
||||
* XMPP server can be reached at for the specified domain.
|
||||
*/
|
||||
public static List<HostAddress> resolveXMPPServerDomain(DnsName domain, List<HostAddress> failedAddresses, DnssecMode dnssecMode) {
|
||||
return resolveDomain(domain, DomainType.server, failedAddresses, dnssecMode);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param domain the domain.
|
||||
* @param domainType the XMPP domain type, server or client.
|
||||
* @param failedAddresses a list that will be populated with host addresses that failed to resolve.
|
||||
* @return a list of resolver host addresses for this domain.
|
||||
*/
|
||||
private static List<HostAddress> resolveDomain(DnsName domain, DomainType domainType,
|
||||
List<HostAddress> failedAddresses, DnssecMode dnssecMode) {
|
||||
if (dnsResolver == null) {
|
||||
throw new IllegalStateException("No DNS Resolver active in Smack");
|
||||
}
|
||||
|
||||
List<HostAddress> addresses = new ArrayList<HostAddress>();
|
||||
|
||||
// Step one: Do SRV lookups
|
||||
DnsName srvDomain = DnsName.from(domainType.srvPrefix, domain);
|
||||
|
||||
List<SRVRecord> srvRecords = dnsResolver.lookupSRVRecords(srvDomain, failedAddresses, dnssecMode);
|
||||
if (srvRecords != null && !srvRecords.isEmpty()) {
|
||||
if (LOGGER.isLoggable(Level.FINE)) {
|
||||
String logMessage = "Resolved SRV RR for " + srvDomain + ":";
|
||||
for (SRVRecord r : srvRecords)
|
||||
logMessage += " " + r;
|
||||
LOGGER.fine(logMessage);
|
||||
}
|
||||
List<HostAddress> sortedRecords = sortSRVRecords(srvRecords);
|
||||
addresses.addAll(sortedRecords);
|
||||
} else {
|
||||
LOGGER.info("Could not resolve DNS SRV resource records for " + srvDomain + ". Consider adding those.");
|
||||
}
|
||||
|
||||
int defaultPort = -1;
|
||||
switch (domainType) {
|
||||
case client:
|
||||
defaultPort = 5222;
|
||||
break;
|
||||
case server:
|
||||
defaultPort = 5269;
|
||||
break;
|
||||
}
|
||||
// Step two: Add the hostname to the end of the list
|
||||
HostAddress hostAddress = dnsResolver.lookupHostAddress(domain, defaultPort, failedAddresses, dnssecMode);
|
||||
if (hostAddress != null) {
|
||||
addresses.add(hostAddress);
|
||||
}
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort a given list of SRVRecords as described in RFC 2782
|
||||
* Note that we follow the RFC with one exception. In a group of the same priority, only the first entry
|
||||
* is calculated by random. The others are ore simply ordered by their priority.
|
||||
*
|
||||
* @param records TODO javadoc me please
|
||||
* @return the list of resolved HostAddresses
|
||||
*/
|
||||
private static List<HostAddress> sortSRVRecords(List<SRVRecord> records) {
|
||||
// RFC 2782, Usage rules: "If there is precisely one SRV RR, and its Target is "."
|
||||
// (the root domain), abort."
|
||||
if (records.size() == 1 && records.get(0).getFQDN().isRootLabel())
|
||||
return Collections.emptyList();
|
||||
|
||||
// sorting the records improves the performance of the bisection later
|
||||
Collections.sort(records);
|
||||
|
||||
// create the priority buckets
|
||||
SortedMap<Integer, List<SRVRecord>> buckets = new TreeMap<Integer, List<SRVRecord>>();
|
||||
for (SRVRecord r : records) {
|
||||
Integer priority = r.getPriority();
|
||||
List<SRVRecord> bucket = buckets.get(priority);
|
||||
// create the list of SRVRecords if it doesn't exist
|
||||
if (bucket == null) {
|
||||
bucket = new LinkedList<SRVRecord>();
|
||||
buckets.put(priority, bucket);
|
||||
}
|
||||
bucket.add(r);
|
||||
}
|
||||
|
||||
List<HostAddress> res = new ArrayList<HostAddress>(records.size());
|
||||
|
||||
for (Integer priority : buckets.keySet()) {
|
||||
List<SRVRecord> bucket = buckets.get(priority);
|
||||
int bucketSize;
|
||||
while ((bucketSize = bucket.size()) > 0) {
|
||||
int[] totals = new int[bucketSize];
|
||||
int running_total = 0;
|
||||
int count = 0;
|
||||
int zeroWeight = 1;
|
||||
|
||||
for (SRVRecord r : bucket) {
|
||||
if (r.getWeight() > 0) {
|
||||
zeroWeight = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (SRVRecord r : bucket) {
|
||||
running_total += r.getWeight() + zeroWeight;
|
||||
totals[count] = running_total;
|
||||
count++;
|
||||
}
|
||||
int selectedPos;
|
||||
if (running_total == 0) {
|
||||
// If running total is 0, then all weights in this priority
|
||||
// group are 0. So we simply select one of the weights randomly
|
||||
// as the other 'normal' algorithm is unable to handle this case
|
||||
selectedPos = (int) (Math.random() * bucketSize);
|
||||
} else {
|
||||
double rnd = Math.random() * running_total;
|
||||
selectedPos = bisect(totals, rnd);
|
||||
}
|
||||
// add the SRVRecord that was randomly chosen on it's weight
|
||||
// to the start of the result list
|
||||
SRVRecord chosenSRVRecord = bucket.remove(selectedPos);
|
||||
res.add(chosenSRVRecord);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// TODO this is not yet really bisection just a stupid linear search
|
||||
private static int bisect(int[] array, double value) {
|
||||
int pos = 0;
|
||||
for (int element : array) {
|
||||
if (value < element)
|
||||
break;
|
||||
pos++;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2019 Florian Schmaus
|
||||
* Copyright 2019-2020 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,4 +20,7 @@ public interface Function<R, T> {
|
|||
|
||||
R apply(T t);
|
||||
|
||||
static <T> Function<T, T> identity() {
|
||||
return t -> t;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,17 +123,26 @@ public class MultiMap<K, V> implements TypedCloneable<MultiMap<K, V>> {
|
|||
}
|
||||
|
||||
public boolean put(K key, V value) {
|
||||
return putInternal(key, list -> list.add(value));
|
||||
}
|
||||
|
||||
public boolean putFirst(K key, V value) {
|
||||
return putInternal(key, list -> list.add(0, value));
|
||||
}
|
||||
|
||||
private boolean putInternal(K key, Consumer<List<V>> valueListConsumer) {
|
||||
boolean keyExisted;
|
||||
List<V> list = map.get(key);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>(ENTRY_LIST_SIZE);
|
||||
list.add(value);
|
||||
map.put(key, list);
|
||||
keyExisted = false;
|
||||
} else {
|
||||
list.add(value);
|
||||
keyExisted = true;
|
||||
}
|
||||
|
||||
valueListConsumer.accept(list);
|
||||
|
||||
return keyExisted;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2003-2007 Jive Software, 2016-2019 Florian Schmaus.
|
||||
* Copyright 2003-2007 Jive Software, 2016-2020 Florian Schmaus.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -467,10 +467,24 @@ public class StringUtils {
|
|||
return sb;
|
||||
}
|
||||
|
||||
public static void appendTo(Collection<? extends Object> collection, StringBuilder sb) {
|
||||
appendTo(collection, ", ", sb);
|
||||
}
|
||||
|
||||
public static <O extends Object> void appendTo(Collection<O> collection, StringBuilder sb,
|
||||
Consumer<O> appendFunction) {
|
||||
appendTo(collection, ", ", sb, appendFunction);
|
||||
}
|
||||
|
||||
public static void appendTo(Collection<? extends Object> collection, String delimiter, StringBuilder sb) {
|
||||
for (Iterator<? extends Object> it = collection.iterator(); it.hasNext();) {
|
||||
Object cs = it.next();
|
||||
sb.append(cs);
|
||||
appendTo(collection, delimiter, sb, o -> sb.append(o));
|
||||
}
|
||||
|
||||
public static <O extends Object> void appendTo(Collection<O> collection, String delimiter, StringBuilder sb,
|
||||
Consumer<O> appendFunction) {
|
||||
for (Iterator<O> it = collection.iterator(); it.hasNext();) {
|
||||
O cs = it.next();
|
||||
appendFunction.accept(cs);
|
||||
if (it.hasNext()) {
|
||||
sb.append(delimiter);
|
||||
}
|
||||
|
@ -565,4 +579,16 @@ public class StringUtils {
|
|||
public static String deleteXmlWhitespace(String string) {
|
||||
return XML_WHITESPACE.matcher(string).replaceAll("");
|
||||
}
|
||||
|
||||
public static Appendable appendHeading(Appendable appendable, String heading) throws IOException {
|
||||
return appendHeading(appendable, heading, '-');
|
||||
}
|
||||
|
||||
public static Appendable appendHeading(Appendable appendable, String heading, char underlineChar) throws IOException {
|
||||
appendable.append(heading).append('\n');
|
||||
for (int i = 0; i < heading.length(); i++) {
|
||||
appendable.append(underlineChar);
|
||||
}
|
||||
return appendable.append('\n');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2013-2018 Florian Schmaus
|
||||
* Copyright 2013-2020 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,13 +19,16 @@ package org.jivesoftware.smack.util.dns;
|
|||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode;
|
||||
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure;
|
||||
|
||||
import org.minidns.dnsname.DnsName;
|
||||
import org.minidns.record.SRV;
|
||||
|
||||
/**
|
||||
* Implementations of this interface define a class that is capable of resolving DNS addresses.
|
||||
|
@ -43,25 +46,25 @@ public abstract class DNSResolver {
|
|||
|
||||
/**
|
||||
* Gets a list of service records for the specified service.
|
||||
*
|
||||
* @param name The symbolic name of the service.
|
||||
* @param failedAddresses list of failed addresses.
|
||||
* @param lookupFailures list of exceptions that occurred during lookup.
|
||||
* @param dnssecMode security mode.
|
||||
* @return The list of SRV records mapped to the service name.
|
||||
*/
|
||||
public final List<SRVRecord> lookupSRVRecords(DnsName name, List<HostAddress> failedAddresses, DnssecMode dnssecMode) {
|
||||
public final Collection<SRV> lookupSrvRecords(DnsName name,
|
||||
List<RemoteConnectionEndpointLookupFailure> lookupFailures, DnssecMode dnssecMode) {
|
||||
checkIfDnssecRequestedAndSupported(dnssecMode);
|
||||
return lookupSRVRecords0(name, failedAddresses, dnssecMode);
|
||||
return lookupSrvRecords0(name, lookupFailures, dnssecMode);
|
||||
}
|
||||
|
||||
protected abstract List<SRVRecord> lookupSRVRecords0(DnsName name, List<HostAddress> failedAddresses, DnssecMode dnssecMode);
|
||||
protected abstract Collection<SRV> lookupSrvRecords0(DnsName name,
|
||||
List<RemoteConnectionEndpointLookupFailure> lookupFailures, DnssecMode dnssecMode);
|
||||
|
||||
public final HostAddress lookupHostAddress(DnsName name, int port, List<HostAddress> failedAddresses, DnssecMode dnssecMode) {
|
||||
public final List<InetAddress> lookupHostAddress(DnsName name,
|
||||
List<RemoteConnectionEndpointLookupFailure> lookupFailures, DnssecMode dnssecMode) {
|
||||
checkIfDnssecRequestedAndSupported(dnssecMode);
|
||||
List<InetAddress> inetAddresses = lookupHostAddress0(name, failedAddresses, dnssecMode);
|
||||
if (inetAddresses == null || inetAddresses.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new HostAddress(name, port, inetAddresses);
|
||||
return lookupHostAddress0(name, lookupFailures, dnssecMode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,11 +77,11 @@ public abstract class DNSResolver {
|
|||
* </p>
|
||||
*
|
||||
* @param name the DNS name to lookup
|
||||
* @param failedAddresses a list with the failed addresses
|
||||
* @param lookupFailures list of exceptions that occurred during lookup.
|
||||
* @param dnssecMode the selected DNSSEC mode
|
||||
* @return A list, either empty or non-empty, or <code>null</code>
|
||||
*/
|
||||
protected List<InetAddress> lookupHostAddress0(DnsName name, List<HostAddress> failedAddresses, DnssecMode dnssecMode) {
|
||||
protected List<InetAddress> lookupHostAddress0(DnsName name, List<RemoteConnectionEndpointLookupFailure> lookupFailures, DnssecMode dnssecMode) {
|
||||
// Default implementation of a DNS name lookup for A/AAAA records. It is assumed that this method does never
|
||||
// support DNSSEC. Subclasses are free to override this method.
|
||||
if (dnssecMode != DnssecMode.disabled) {
|
||||
|
@ -89,14 +92,14 @@ public abstract class DNSResolver {
|
|||
try {
|
||||
inetAddressArray = InetAddress.getAllByName(name.toString());
|
||||
} catch (UnknownHostException e) {
|
||||
failedAddresses.add(new HostAddress(name, e));
|
||||
lookupFailures.add(new RemoteConnectionEndpointLookupFailure.DnsLookupFailure(name, e));
|
||||
return null;
|
||||
}
|
||||
|
||||
return Arrays.asList(inetAddressArray);
|
||||
}
|
||||
|
||||
protected final boolean shouldContinue(CharSequence name, CharSequence hostname, List<InetAddress> hostAddresses) {
|
||||
protected static boolean shouldContinue(CharSequence name, CharSequence hostname, List<InetAddress> hostAddresses) {
|
||||
if (hostAddresses == null) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,182 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright © 2013-2018 Florian Schmaus
|
||||
*
|
||||
* 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.util.dns;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.jivesoftware.smack.SmackException.ConnectionException;
|
||||
|
||||
import org.minidns.dnsname.DnsName;
|
||||
|
||||
public class HostAddress {
|
||||
private final DnsName fqdn;
|
||||
private final int port;
|
||||
private final Map<InetAddress, Exception> exceptions = new LinkedHashMap<>();
|
||||
private final List<InetAddress> inetAddresses;
|
||||
|
||||
/**
|
||||
* Creates a new HostAddress with the given FQDN.
|
||||
*
|
||||
* @param fqdn the optional fully qualified domain name (FQDN).
|
||||
* @param port The port to connect on.
|
||||
* @param inetAddresses list of addresses.
|
||||
* @throws IllegalArgumentException If the port is out of valid range (0 - 65535).
|
||||
*/
|
||||
public HostAddress(DnsName fqdn, int port, List<InetAddress> inetAddresses) {
|
||||
if (port < 0 || port > 65535)
|
||||
throw new IllegalArgumentException(
|
||||
"Port must be a 16-bit unsigned integer (i.e. between 0-65535. Port was: " + port);
|
||||
this.fqdn = fqdn;
|
||||
this.port = port;
|
||||
if (inetAddresses.isEmpty()) {
|
||||
throw new IllegalArgumentException("Must provide at least one InetAddress");
|
||||
}
|
||||
this.inetAddresses = inetAddresses;
|
||||
}
|
||||
|
||||
public HostAddress(int port, InetAddress hostAddress) {
|
||||
this(null, port, Collections.singletonList(hostAddress));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new failed HostAddress. This constructor is usually used when the DNS resolution of the domain name
|
||||
* failed for some reason.
|
||||
*
|
||||
* @param fqdn the domain name of the host.
|
||||
* @param e the exception causing the failure.
|
||||
*/
|
||||
public HostAddress(DnsName fqdn, Exception e) {
|
||||
this.fqdn = fqdn;
|
||||
this.port = 5222;
|
||||
inetAddresses = Collections.emptyList();
|
||||
setException(e);
|
||||
}
|
||||
|
||||
public HostAddress(InetSocketAddress inetSocketAddress, Exception exception) {
|
||||
String hostString = inetSocketAddress.getHostString();
|
||||
this.fqdn = DnsName.from(hostString);
|
||||
this.port = inetSocketAddress.getPort();
|
||||
inetAddresses = Collections.emptyList();
|
||||
setException(exception);
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
if (fqdn != null) {
|
||||
return fqdn.toString();
|
||||
}
|
||||
|
||||
// In this case, the HostAddress(int, InetAddress) constructor must been used. We have no FQDN. And
|
||||
// inetAddresses.size() must be exactly one.
|
||||
assert inetAddresses.size() == 1;
|
||||
return inetAddresses.get(0).getHostAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fully qualified domain name. This may return <code>null</code> in case there host address is only numeric, i.e. an IP address.
|
||||
*
|
||||
* @return the fully qualified domain name or <code>null</code>
|
||||
*/
|
||||
public DnsName getFQDN() {
|
||||
return fqdn;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setException(Exception exception) {
|
||||
setException(null, exception);
|
||||
}
|
||||
|
||||
public void setException(InetAddress inetAddress, Exception exception) {
|
||||
Exception old = exceptions.put(inetAddress, exception);
|
||||
assert old == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Exception that caused a connection failure to this HostAddress. Every
|
||||
* HostAddress found in {@link ConnectionException} will have an Exception set,
|
||||
* which can be retrieved with this method.
|
||||
*
|
||||
* @return the Exception causing this HostAddress to fail
|
||||
*/
|
||||
public Map<InetAddress, Exception> getExceptions() {
|
||||
return Collections.unmodifiableMap(exceptions);
|
||||
}
|
||||
|
||||
public List<InetAddress> getInetAddresses() {
|
||||
return Collections.unmodifiableList(inetAddresses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getHost() + ":" + port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof HostAddress)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final HostAddress address = (HostAddress) o;
|
||||
|
||||
if (!getHost().equals(address.getHost())) {
|
||||
return false;
|
||||
}
|
||||
return port == address.port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 37 * result + getHost().hashCode();
|
||||
return result * 37 + port;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
if (exceptions.isEmpty()) {
|
||||
return "No error logged";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('\'').append(toString()).append("' failed because: ");
|
||||
Iterator<Entry<InetAddress, Exception>> iterator = exceptions.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Entry<InetAddress, Exception> entry = iterator.next();
|
||||
InetAddress inetAddress = entry.getKey();
|
||||
if (inetAddress != null) {
|
||||
sb.append(entry.getKey()).append(" exception: ");
|
||||
}
|
||||
sb.append(entry.getValue());
|
||||
if (iterator.hasNext()) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2013-2018 Florian Schmaus
|
||||
*
|
||||
* 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.util.dns;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
import org.minidns.dnsname.DnsName;
|
||||
|
||||
/**
|
||||
* A DNS SRV RR.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2782">RFC 2782: A DNS RR for specifying the location of services (DNS
|
||||
* SRV)</a>
|
||||
* @author Florian Schmaus
|
||||
*
|
||||
*/
|
||||
public class SRVRecord extends HostAddress implements Comparable<SRVRecord> {
|
||||
|
||||
private int weight;
|
||||
private int priority;
|
||||
|
||||
/**
|
||||
* SRV Record constructor.
|
||||
*
|
||||
* @param fqdn Fully qualified domain name
|
||||
* @param port The connection port
|
||||
* @param priority Priority of the target host
|
||||
* @param weight Relative weight for records with same priority
|
||||
* @param inetAddresses list of addresses.
|
||||
* @throws IllegalArgumentException fqdn is null or any other field is not in valid range (0-65535).
|
||||
*/
|
||||
public SRVRecord(DnsName fqdn, int port, int priority, int weight, List<InetAddress> inetAddresses) {
|
||||
super(fqdn, port, inetAddresses);
|
||||
StringUtils.requireNotNullNorEmpty(fqdn, "The FQDN must not be null");
|
||||
if (weight < 0 || weight > 65535)
|
||||
throw new IllegalArgumentException(
|
||||
"DNS SRV records weight must be a 16-bit unsigned integer (i.e. between 0-65535. Weight was: "
|
||||
+ weight);
|
||||
|
||||
if (priority < 0 || priority > 65535)
|
||||
throw new IllegalArgumentException(
|
||||
"DNS SRV records priority must be a 16-bit unsigned integer (i.e. between 0-65535. Priority was: "
|
||||
+ priority);
|
||||
|
||||
this.priority = priority;
|
||||
this.weight = weight;
|
||||
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public int getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(SRVRecord other) {
|
||||
// According to RFC2782,
|
||||
// "[a] client MUST attempt to contact the target host with the lowest-numbered priority it can reach".
|
||||
// This means that a SRV record with a higher priority is 'less' then one with a lower.
|
||||
int res = other.priority - this.priority;
|
||||
if (res == 0) {
|
||||
res = this.weight - other.weight;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " prio:" + priority + ":w:" + weight;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2020 Florian Schmaus
|
||||
*
|
||||
* 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.util.rce;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.jivesoftware.smack.datatypes.UInt16;
|
||||
|
||||
public interface RemoteConnectionEndpoint {
|
||||
|
||||
CharSequence getHost();
|
||||
|
||||
UInt16 getPort();
|
||||
|
||||
Collection<? extends InetAddress> getInetAddresses();
|
||||
|
||||
String getDescription();
|
||||
|
||||
class InetSocketAddressCoupling<RCE extends RemoteConnectionEndpoint> {
|
||||
private final RCE connectionEndpoint;
|
||||
private final InetSocketAddress inetSocketAddress;
|
||||
|
||||
public InetSocketAddressCoupling(RCE connectionEndpoint, InetAddress inetAddress) {
|
||||
this.connectionEndpoint = connectionEndpoint;
|
||||
|
||||
UInt16 port = connectionEndpoint.getPort();
|
||||
inetSocketAddress = new InetSocketAddress(inetAddress, port.intValue());
|
||||
}
|
||||
|
||||
public RCE getRemoteConnectionEndpoint() {
|
||||
return connectionEndpoint;
|
||||
}
|
||||
|
||||
public InetSocketAddress getInetSocketAddress() {
|
||||
return inetSocketAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return connectionEndpoint.getDescription() + " (" + inetSocketAddress + ')';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2020 Florian Schmaus
|
||||
*
|
||||
* 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.util.rce;
|
||||
|
||||
import org.jivesoftware.smack.util.ToStringUtil;
|
||||
|
||||
import org.minidns.dnsname.DnsName;
|
||||
|
||||
public abstract class RemoteConnectionEndpointLookupFailure {
|
||||
|
||||
private final String description;
|
||||
private final Exception exception;
|
||||
|
||||
public RemoteConnectionEndpointLookupFailure(String description, Exception exception) {
|
||||
this.description = description;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public final String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public final Exception getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return description + " because: " + exception;
|
||||
}
|
||||
|
||||
private transient String toStringCache;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (toStringCache == null) {
|
||||
toStringCache = ToStringUtil.builderFor(RemoteConnectionEndpointLookupFailure.class)
|
||||
.addValue("description", description)
|
||||
.addValue("exception", exception)
|
||||
.build();
|
||||
}
|
||||
return toStringCache;
|
||||
}
|
||||
|
||||
public static class DnsLookupFailure extends RemoteConnectionEndpointLookupFailure {
|
||||
private final DnsName dnsName;
|
||||
|
||||
public DnsLookupFailure(DnsName dnsName, Exception exception) {
|
||||
super("DNS lookup exception for " + dnsName, exception);
|
||||
this.dnsName = dnsName;
|
||||
}
|
||||
|
||||
public DnsName getDnsName() {
|
||||
return dnsName;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2020 Florian Schmaus
|
||||
*
|
||||
* 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.util.rce;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
import org.jivesoftware.smack.util.ToStringUtil;
|
||||
|
||||
public final class RemoteConnectionException<RCE extends RemoteConnectionEndpoint> {
|
||||
|
||||
private final RemoteConnectionEndpoint.InetSocketAddressCoupling<RCE> address;
|
||||
private final Exception exception;
|
||||
|
||||
public RemoteConnectionException(RCE remoteConnectionEndpoint, InetAddress inetAddress,
|
||||
Exception exception) {
|
||||
this(new RemoteConnectionEndpoint.InetSocketAddressCoupling<>(remoteConnectionEndpoint, inetAddress), exception);
|
||||
}
|
||||
|
||||
public RemoteConnectionException(RemoteConnectionEndpoint.InetSocketAddressCoupling<RCE> address, Exception exception) {
|
||||
this.address = address;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public RemoteConnectionEndpoint.InetSocketAddressCoupling<RCE> getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public Exception getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return "\'" + address + "' failed because: " + exception;
|
||||
}
|
||||
|
||||
private transient String toStringCache;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (toStringCache == null) {
|
||||
toStringCache = ToStringUtil.builderFor(RemoteConnectionException.class)
|
||||
.addValue("address", address)
|
||||
.addValue("exception", exception)
|
||||
.build();
|
||||
}
|
||||
return toStringCache;
|
||||
}
|
||||
|
||||
public static <SARCE extends SingleAddressRemoteConnectionEndpoint> RemoteConnectionException<SARCE> from(SARCE remoteConnectionEndpoint, Exception exception) {
|
||||
return new RemoteConnectionException<SARCE>(remoteConnectionEndpoint, remoteConnectionEndpoint.getInetAddress(), exception);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2020 Florian Schmaus
|
||||
*
|
||||
* 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.util.rce;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
public interface SingleAddressRemoteConnectionEndpoint extends RemoteConnectionEndpoint {
|
||||
|
||||
InetAddress getInetAddress();
|
||||
|
||||
@Override
|
||||
default Collection<? extends InetAddress> getInetAddresses() {
|
||||
return Collections.singletonList(getInetAddress());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2020 Florian Schmaus
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Utility classes for Remote Connection Endpoints (RCE).
|
||||
*/
|
||||
package org.jivesoftware.smack.util.rce;
|
|
@ -1,59 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright © 2014-2018 Florian Schmaus
|
||||
*
|
||||
* 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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jivesoftware.smack.SmackException.ConnectionException;
|
||||
import org.jivesoftware.smack.util.dns.HostAddress;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.minidns.dnsname.DnsName;
|
||||
|
||||
public class SmackExceptionTest {
|
||||
|
||||
@Test
|
||||
public void testConnectionException() throws UnknownHostException {
|
||||
List<HostAddress> failedAddresses = new LinkedList<HostAddress>();
|
||||
|
||||
DnsName host = DnsName.from("foo.bar.example");
|
||||
InetAddress inetAddress = InetAddress.getByAddress(host.toString(), new byte[] { 0, 0, 0, 0 });
|
||||
List<InetAddress> inetAddresses = Collections.singletonList(inetAddress);
|
||||
HostAddress hostAddress = new HostAddress(host, 1234, inetAddresses);
|
||||
hostAddress.setException(new Exception("Failed for some reason"));
|
||||
failedAddresses.add(hostAddress);
|
||||
|
||||
host = DnsName.from("barz.example");
|
||||
inetAddress = InetAddress.getByAddress(host.toString(), new byte[] { 0, 0, 0, 0 });
|
||||
inetAddresses = Collections.singletonList(inetAddress);
|
||||
hostAddress = new HostAddress(host, 5678, inetAddresses);
|
||||
hostAddress.setException(new Exception("Failed for some other reason"));
|
||||
failedAddresses.add(hostAddress);
|
||||
|
||||
ConnectionException connectionException = ConnectionException.from(failedAddresses);
|
||||
String message = connectionException.getMessage();
|
||||
assertEquals("The following addresses failed: 'foo.bar.example:1234' failed because: java.lang.Exception: Failed for some reason, 'barz.example:5678' failed because: java.lang.Exception: Failed for some other reason",
|
||||
message);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Florian Schmaus.
|
||||
* Copyright 2018-2020 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,6 @@ package org.jivesoftware.smack.util;
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.jivesoftware.smack.util.DNSUtil.DomainType;
|
||||
import org.jivesoftware.smack.util.dns.SmackDaneProvider;
|
||||
import org.jivesoftware.smack.util.dns.SmackDaneVerifier;
|
||||
|
||||
|
@ -26,15 +25,6 @@ import org.junit.Test;
|
|||
|
||||
public class DnsUtilTest {
|
||||
|
||||
@Test
|
||||
public void simpleDomainTypeTest() {
|
||||
DomainType client = DomainType.client;
|
||||
assertEquals(DNSUtil.XMPP_CLIENT_DNS_SRV_PREFIX, client.srvPrefix.ace);
|
||||
|
||||
DomainType server = DomainType.server;
|
||||
assertEquals(DNSUtil.XMPP_SERVER_DNS_SRV_PREFIX, server.srvPrefix.ace);
|
||||
}
|
||||
|
||||
private static final SmackDaneProvider DNS_UTIL_TEST_DANE_PROVIDER = new SmackDaneProvider() {
|
||||
@Override
|
||||
public SmackDaneVerifier newInstance() {
|
||||
|
|
|
@ -26,10 +26,6 @@ public class StanzaIdProvider extends ExtensionElementProvider<StanzaIdElement>
|
|||
|
||||
public static final StanzaIdProvider INSTANCE = new StanzaIdProvider();
|
||||
|
||||
// TODO: Remove in Smack 4.4.
|
||||
@Deprecated
|
||||
public static final StanzaIdProvider TEST_INSTANCE = INSTANCE;
|
||||
|
||||
@Override
|
||||
public StanzaIdElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) {
|
||||
String id = parser.getAttributeValue(null, StanzaIdElement.ATTR_ID);
|
||||
|
|
|
@ -2,24 +2,7 @@
|
|||
<!-- Providers for workgroup extensions -->
|
||||
<smackProviders>
|
||||
|
||||
<!-- XEP-0332 HTTP over XMPP transport -->
|
||||
<iqProvider>
|
||||
<elementName>req</elementName>
|
||||
<namespace>urn:xmpp:http</namespace>
|
||||
<className>org.jivesoftware.smackx.hoxt.provider.HttpOverXmppReqProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>resp</elementName>
|
||||
<namespace>urn:xmpp:http</namespace>
|
||||
<className>org.jivesoftware.smackx.hoxt.provider.HttpOverXmppRespProvider</className>
|
||||
</iqProvider>
|
||||
<extensionProvider>
|
||||
<elementName>chunk</elementName>
|
||||
<namespace>urn:xmpp:http</namespace>
|
||||
<className>org.jivesoftware.smackx.hoxt.provider.Base64BinaryChunkProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0280 Message Carbons -->
|
||||
<!-- XEP-0280: Message Carbons -->
|
||||
<extensionProvider>
|
||||
<elementName>sent</elementName>
|
||||
<namespace>urn:xmpp:carbons:2</namespace>
|
||||
|
@ -31,60 +14,7 @@
|
|||
<className>org.jivesoftware.smackx.carbons.provider.CarbonManagerProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0352 Client State Indication -->
|
||||
<streamFeatureProvider>
|
||||
<elementName>csi</elementName>
|
||||
<namespace>urn:xmpp:csi:0</namespace>
|
||||
<className>org.jivesoftware.smackx.csi.provider.ClientStateIndicationFeatureProvider</className>
|
||||
</streamFeatureProvider>
|
||||
|
||||
<!-- XEP-0335 JSON Containers -->
|
||||
<extensionProvider>
|
||||
<elementName>json</elementName>
|
||||
<namespace>urn:xmpp:json:0</namespace>
|
||||
<className>org.jivesoftware.smackx.json.provider.JsonExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- GCM JSON payload -->
|
||||
<extensionProvider>
|
||||
<elementName>gcm</elementName>
|
||||
<namespace>google:mobile:data</namespace>
|
||||
<className>org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-xxxx: Multi-User Chat Light -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>urn:xmpp:muclight:0#info</namespace>
|
||||
<className>org.jivesoftware.smackx.muclight.provider.MUCLightInfoIQProvider</className>
|
||||
</iqProvider>
|
||||
<extensionProvider>
|
||||
<elementName>x</elementName>
|
||||
<namespace>urn:xmpp:muclight:0#affiliations</namespace>
|
||||
<className>org.jivesoftware.smackx.muclight.provider.MUCLightAffiliationsChangeProvider</className>
|
||||
</extensionProvider>
|
||||
<extensionProvider>
|
||||
<elementName>x</elementName>
|
||||
<namespace>urn:xmpp:muclight:0#configuration</namespace>
|
||||
<className>org.jivesoftware.smackx.muclight.provider.MUCLightConfigurationsChangeProvider</className>
|
||||
</extensionProvider>
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>urn:xmpp:muclight:0#configuration</namespace>
|
||||
<className>org.jivesoftware.smackx.muclight.provider.MUCLightConfigurationIQProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>urn:xmpp:muclight:0#affiliations</namespace>
|
||||
<className>org.jivesoftware.smackx.muclight.provider.MUCLightAffiliationsIQProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>urn:xmpp:muclight:0#blocking</namespace>
|
||||
<className>org.jivesoftware.smackx.muclight.provider.MUCLightBlockingIQProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0313 Message Archive Management -->
|
||||
<!-- XEP-0313: Message Archive Management -->
|
||||
<iqProvider>
|
||||
<elementName>prefs</elementName>
|
||||
<namespace>urn:xmpp:mam:1</namespace>
|
||||
|
@ -106,42 +36,22 @@
|
|||
<className>org.jivesoftware.smackx.mam.provider.MamResultProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0347: Internet of Things - Discovery -->
|
||||
<!-- XEP-0323: Internet of Things - Data -->
|
||||
<iqProvider>
|
||||
<elementName>register</elementName>
|
||||
<namespace>urn:xmpp:iot:discovery</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTRegisterProvider</className>
|
||||
<elementName>req</elementName>
|
||||
<namespace>urn:xmpp:iot:sensordata</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.data.provider.IoTDataRequestProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>claimed</elementName>
|
||||
<namespace>urn:xmpp:iot:discovery</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTClaimedProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>disown</elementName>
|
||||
<namespace>urn:xmpp:iot:discovery</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTDisownProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>disowned</elementName>
|
||||
<namespace>urn:xmpp:iot:discovery</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTDisownedProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>remove</elementName>
|
||||
<namespace>urn:xmpp:iot:discovery</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTRemoveProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>disown</elementName>
|
||||
<namespace>urn:xmpp:iot:discovery</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTRemovedProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>unregister</elementName>
|
||||
<namespace>urn:xmpp:iot:discovery</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTUnregisterProvider</className>
|
||||
<elementName>accepted</elementName>
|
||||
<namespace>urn:xmpp:iot:sensordata</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.data.provider.IoTDataReadOutAcceptedProvider</className>
|
||||
</iqProvider>
|
||||
<extensionProvider>
|
||||
<elementName>fields</elementName>
|
||||
<namespace>urn:xmpp:iot:sensordata</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.data.provider.IoTFieldsExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0324: Internet of Things - Provisioning -->
|
||||
<iqProvider>
|
||||
|
@ -175,23 +85,6 @@
|
|||
<className>org.jivesoftware.smackx.iot.provisioning.provider.UnfriendProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0323: Internet of Things - Data -->
|
||||
<iqProvider>
|
||||
<elementName>req</elementName>
|
||||
<namespace>urn:xmpp:iot:sensordata</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.data.provider.IoTDataRequestProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>accepted</elementName>
|
||||
<namespace>urn:xmpp:iot:sensordata</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.data.provider.IoTDataReadOutAcceptedProvider</className>
|
||||
</iqProvider>
|
||||
<extensionProvider>
|
||||
<elementName>fields</elementName>
|
||||
<namespace>urn:xmpp:iot:sensordata</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.data.provider.IoTFieldsExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0325: Internet of Things - Control -->
|
||||
<iqProvider>
|
||||
<elementName>set</elementName>
|
||||
|
@ -204,23 +97,28 @@
|
|||
<className>org.jivesoftware.smackx.iot.control.provider.IoTSetResponseProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0357 Push Notifications -->
|
||||
<extensionProvider>
|
||||
<elementName>pubsub</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub</namespace>
|
||||
<className>org.jivesoftware.smackx.push_notifications.provider.RemoteDisablingProvider</className>
|
||||
</extensionProvider>
|
||||
<!-- XEP-0328: JID Preparation and Validation Service (JID Prep) -->
|
||||
<iqProvider>
|
||||
<elementName>jid</elementName>
|
||||
<namespace>urn:xmpp:jidprep:0</namespace>
|
||||
<className>org.jivesoftware.smackx.jid_prep.provider.JidPrepIqProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0359: Stable and Unique Stanza IDs -->
|
||||
<!-- XEP-0332: HTTP over XMPP transport -->
|
||||
<iqProvider>
|
||||
<elementName>req</elementName>
|
||||
<namespace>urn:xmpp:http</namespace>
|
||||
<className>org.jivesoftware.smackx.hoxt.provider.HttpOverXmppReqProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>resp</elementName>
|
||||
<namespace>urn:xmpp:http</namespace>
|
||||
<className>org.jivesoftware.smackx.hoxt.provider.HttpOverXmppRespProvider</className>
|
||||
</iqProvider>
|
||||
<extensionProvider>
|
||||
<elementName>stanza-id</elementName>
|
||||
<namespace>urn:xmpp:sid:0</namespace>
|
||||
<className>org.jivesoftware.smackx.sid.provider.StanzaIdProvider</className>
|
||||
</extensionProvider>
|
||||
<extensionProvider>
|
||||
<elementName>origin-id</elementName>
|
||||
<namespace>urn:xmpp:sid:0</namespace>
|
||||
<className>org.jivesoftware.smackx.sid.provider.OriginIdProvider</className>
|
||||
<elementName>chunk</elementName>
|
||||
<namespace>urn:xmpp:http</namespace>
|
||||
<className>org.jivesoftware.smackx.hoxt.provider.Base64BinaryChunkProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0333: Chat Markers -->
|
||||
|
@ -267,6 +165,76 @@
|
|||
<className>org.jivesoftware.smackx.hints.provider.StoreHintProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0335: JSON Containers -->
|
||||
<extensionProvider>
|
||||
<elementName>json</elementName>
|
||||
<namespace>urn:xmpp:json:0</namespace>
|
||||
<className>org.jivesoftware.smackx.json.provider.JsonExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0347: Internet of Things - Discovery -->
|
||||
<iqProvider>
|
||||
<elementName>register</elementName>
|
||||
<namespace>urn:xmpp:iot:discovery</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTRegisterProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>claimed</elementName>
|
||||
<namespace>urn:xmpp:iot:discovery</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTClaimedProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>disown</elementName>
|
||||
<namespace>urn:xmpp:iot:discovery</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTDisownProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>disowned</elementName>
|
||||
<namespace>urn:xmpp:iot:discovery</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTDisownedProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>remove</elementName>
|
||||
<namespace>urn:xmpp:iot:discovery</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTRemoveProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>disown</elementName>
|
||||
<namespace>urn:xmpp:iot:discovery</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTRemovedProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>unregister</elementName>
|
||||
<namespace>urn:xmpp:iot:discovery</namespace>
|
||||
<className>org.jivesoftware.smackx.iot.discovery.provider.IoTUnregisterProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0352: Client State Indication -->
|
||||
<streamFeatureProvider>
|
||||
<elementName>csi</elementName>
|
||||
<namespace>urn:xmpp:csi:0</namespace>
|
||||
<className>org.jivesoftware.smackx.csi.provider.ClientStateIndicationFeatureProvider</className>
|
||||
</streamFeatureProvider>
|
||||
|
||||
<!-- XEP-0357: Push Notifications -->
|
||||
<extensionProvider>
|
||||
<elementName>pubsub</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub</namespace>
|
||||
<className>org.jivesoftware.smackx.push_notifications.provider.RemoteDisablingProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0359: Stable and Unique Stanza IDs -->
|
||||
<extensionProvider>
|
||||
<elementName>stanza-id</elementName>
|
||||
<namespace>urn:xmpp:sid:0</namespace>
|
||||
<className>org.jivesoftware.smackx.sid.provider.StanzaIdProvider</className>
|
||||
</extensionProvider>
|
||||
<extensionProvider>
|
||||
<elementName>origin-id</elementName>
|
||||
<namespace>urn:xmpp:sid:0</namespace>
|
||||
<className>org.jivesoftware.smackx.sid.provider.OriginIdProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0363: HTTP File Upload -->
|
||||
<iqProvider>
|
||||
<elementName>slot</elementName>
|
||||
|
@ -324,11 +292,46 @@
|
|||
<className>org.jivesoftware.smackx.dox.provider.DnsIqProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0328: JID Prep -->
|
||||
|
||||
<!-- XEP-xxxx: Multi-User Chat Light -->
|
||||
<iqProvider>
|
||||
<elementName>jid</elementName>
|
||||
<namespace>urn:xmpp:jidprep:0</namespace>
|
||||
<className>org.jivesoftware.smackx.jid_prep.provider.JidPrepIqProvider</className>
|
||||
<elementName>query</elementName>
|
||||
<namespace>urn:xmpp:muclight:0#info</namespace>
|
||||
<className>org.jivesoftware.smackx.muclight.provider.MUCLightInfoIQProvider</className>
|
||||
</iqProvider>
|
||||
<extensionProvider>
|
||||
<elementName>x</elementName>
|
||||
<namespace>urn:xmpp:muclight:0#affiliations</namespace>
|
||||
<className>org.jivesoftware.smackx.muclight.provider.MUCLightAffiliationsChangeProvider</className>
|
||||
</extensionProvider>
|
||||
<extensionProvider>
|
||||
<elementName>x</elementName>
|
||||
<namespace>urn:xmpp:muclight:0#configuration</namespace>
|
||||
<className>org.jivesoftware.smackx.muclight.provider.MUCLightConfigurationsChangeProvider</className>
|
||||
</extensionProvider>
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>urn:xmpp:muclight:0#configuration</namespace>
|
||||
<className>org.jivesoftware.smackx.muclight.provider.MUCLightConfigurationIQProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>urn:xmpp:muclight:0#affiliations</namespace>
|
||||
<className>org.jivesoftware.smackx.muclight.provider.MUCLightAffiliationsIQProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>urn:xmpp:muclight:0#blocking</namespace>
|
||||
<className>org.jivesoftware.smackx.muclight.provider.MUCLightBlockingIQProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
|
||||
<!-- GCM JSON payload -->
|
||||
<extensionProvider>
|
||||
<elementName>gcm</elementName>
|
||||
<namespace>google:mobile:data</namespace>
|
||||
<className>org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
|
||||
</smackProviders>
|
||||
|
|
|
@ -2,426 +2,442 @@
|
|||
<!-- Providers file for default Smack extensions -->
|
||||
<smackProviders>
|
||||
|
||||
<!-- Private Data Storage -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:private</namespace>
|
||||
<className>org.jivesoftware.smackx.iqprivate.PrivateDataManager$PrivateDataIQProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- Time -->
|
||||
<iqProvider>
|
||||
<elementName>time</elementName>
|
||||
<namespace>urn:xmpp:time</namespace>
|
||||
<className>org.jivesoftware.smackx.time.provider.TimeProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0085: Chat State -->
|
||||
<extensionProvider>
|
||||
<elementName>active</elementName>
|
||||
<namespace>http://jabber.org/protocol/chatstates</namespace>
|
||||
<className>org.jivesoftware.smackx.chatstates.provider.ChatStateExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>composing</elementName>
|
||||
<namespace>http://jabber.org/protocol/chatstates</namespace>
|
||||
<className>org.jivesoftware.smackx.chatstates.provider.ChatStateExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>paused</elementName>
|
||||
<namespace>http://jabber.org/protocol/chatstates</namespace>
|
||||
<className>org.jivesoftware.smackx.chatstates.provider.ChatStateExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>inactive</elementName>
|
||||
<namespace>http://jabber.org/protocol/chatstates</namespace>
|
||||
<className>org.jivesoftware.smackx.chatstates.provider.ChatStateExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>gone</elementName>
|
||||
<namespace>http://jabber.org/protocol/chatstates</namespace>
|
||||
<className>org.jivesoftware.smackx.chatstates.provider.ChatStateExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XHTML -->
|
||||
<extensionProvider>
|
||||
<elementName>html</elementName>
|
||||
<namespace>http://jabber.org/protocol/xhtml-im</namespace>
|
||||
<className>org.jivesoftware.smackx.xhtmlim.provider.XHTMLExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- Group Chat Invitations -->
|
||||
<extensionProvider>
|
||||
<elementName>x</elementName>
|
||||
<namespace>jabber:x:conference</namespace>
|
||||
<className>org.jivesoftware.smackx.muc.packet.GroupChatInvitation$Provider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- Service Discovery # Items -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>http://jabber.org/protocol/disco#items</namespace>
|
||||
<className>org.jivesoftware.smackx.disco.provider.DiscoverItemsProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- Service Discovery # Info -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>http://jabber.org/protocol/disco#info</namespace>
|
||||
<className>org.jivesoftware.smackx.disco.provider.DiscoverInfoProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- Data Forms-->
|
||||
<!-- XEP-0004: Data Forms-->
|
||||
<extensionProvider>
|
||||
<elementName>x</elementName>
|
||||
<namespace>jabber:x:data</namespace>
|
||||
<className>org.jivesoftware.smackx.xdata.provider.DataFormProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- MUC User -->
|
||||
<extensionProvider>
|
||||
<elementName>x</elementName>
|
||||
<namespace>http://jabber.org/protocol/muc#user</namespace>
|
||||
<className>org.jivesoftware.smackx.muc.provider.MUCUserProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- MUC Admin -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>http://jabber.org/protocol/muc#admin</namespace>
|
||||
<className>org.jivesoftware.smackx.muc.provider.MUCAdminProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- MUC Owner -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>http://jabber.org/protocol/muc#owner</namespace>
|
||||
<className>org.jivesoftware.smackx.muc.provider.MUCOwnerProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- Delayed Delivery -->
|
||||
<extensionProvider>
|
||||
<elementName>x</elementName>
|
||||
<namespace>jabber:x:delay</namespace>
|
||||
<className>org.jivesoftware.smackx.delay.provider.LegacyDelayInformationProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>delay</elementName>
|
||||
<namespace>urn:xmpp:delay</namespace>
|
||||
<className>org.jivesoftware.smackx.delay.provider.DelayInformationProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- Version -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:version</namespace>
|
||||
<className>org.jivesoftware.smackx.iqversion.provider.VersionProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- VCard -->
|
||||
<iqProvider>
|
||||
<elementName>vCard</elementName>
|
||||
<namespace>vcard-temp</namespace>
|
||||
<className>org.jivesoftware.smackx.vcardtemp.provider.VCardProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- Offline Message Requests -->
|
||||
<iqProvider>
|
||||
<elementName>offline</elementName>
|
||||
<namespace>http://jabber.org/protocol/offline</namespace>
|
||||
<className>org.jivesoftware.smackx.offline.packet.OfflineMessageRequest$Provider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- Offline Message Indicator -->
|
||||
<extensionProvider>
|
||||
<elementName>offline</elementName>
|
||||
<namespace>http://jabber.org/protocol/offline</namespace>
|
||||
<className>org.jivesoftware.smackx.offline.packet.OfflineMessageInfo$Provider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- Last Activity -->
|
||||
<!-- XEP-0012: Last Activity -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:last</namespace>
|
||||
<className>org.jivesoftware.smackx.iqlast.packet.LastActivity$Provider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- User Search -->
|
||||
<!-- XEP-0013: Flexible Offline Message Retrieval -->
|
||||
<iqProvider>
|
||||
<elementName>offline</elementName>
|
||||
<namespace>http://jabber.org/protocol/offline</namespace>
|
||||
<className>org.jivesoftware.smackx.offline.packet.OfflineMessageRequest$Provider</className>
|
||||
</iqProvider>
|
||||
<extensionProvider>
|
||||
<elementName>offline</elementName>
|
||||
<namespace>http://jabber.org/protocol/offline</namespace>
|
||||
<className>org.jivesoftware.smackx.offline.packet.OfflineMessageInfo$Provider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0016: Privacy Lists -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:search</namespace>
|
||||
<className>org.jivesoftware.smackx.search.UserSearch$Provider</className>
|
||||
<namespace>jabber:iq:privacy</namespace>
|
||||
<className>org.jivesoftware.smackx.privacy.provider.PrivacyProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- SharedGroupsInfo -->
|
||||
<!-- XEP-0030: Service Discovery -->
|
||||
<iqProvider>
|
||||
<elementName>sharedgroup</elementName>
|
||||
<namespace>http://www.jivesoftware.org/protocol/sharedgroup</namespace>
|
||||
<className>org.jivesoftware.smackx.sharedgroups.packet.SharedGroupsInfo$Provider</className>
|
||||
<elementName>query</elementName>
|
||||
<namespace>http://jabber.org/protocol/disco#items</namespace>
|
||||
<className>org.jivesoftware.smackx.disco.provider.DiscoverItemsProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>http://jabber.org/protocol/disco#info</namespace>
|
||||
<className>org.jivesoftware.smackx.disco.provider.DiscoverInfoProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-33: Extended Stanza Addressing -->
|
||||
<!-- XEP-0033: Extended Stanza Addressing -->
|
||||
<extensionProvider>
|
||||
<elementName>addresses</elementName>
|
||||
<namespace>http://jabber.org/protocol/address</namespace>
|
||||
<className>org.jivesoftware.smackx.address.provider.MultipleAddressesProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- FileTransfer -->
|
||||
<iqProvider>
|
||||
<elementName>si</elementName>
|
||||
<namespace>http://jabber.org/protocol/si</namespace>
|
||||
<className>org.jivesoftware.smackx.si.provider.StreamInitiationProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0045: Multi-User Chat -->
|
||||
<extensionProvider>
|
||||
<elementName>x</elementName>
|
||||
<namespace>http://jabber.org/protocol/muc#user</namespace>
|
||||
<className>org.jivesoftware.smackx.muc.provider.MUCUserProvider</className>
|
||||
</extensionProvider>
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>http://jabber.org/protocol/bytestreams</namespace>
|
||||
<className>org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider</className>
|
||||
<namespace>http://jabber.org/protocol/muc#admin</namespace>
|
||||
<className>org.jivesoftware.smackx.muc.provider.MUCAdminProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>http://jabber.org/protocol/muc#owner</namespace>
|
||||
<className>org.jivesoftware.smackx.muc.provider.MUCOwnerProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0047: In-Band Bytestreams -->
|
||||
<iqProvider>
|
||||
<elementName>open</elementName>
|
||||
<namespace>http://jabber.org/protocol/ibb</namespace>
|
||||
<className>org.jivesoftware.smackx.bytestreams.ibb.provider.OpenIQProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<iqProvider>
|
||||
<elementName>data</elementName>
|
||||
<namespace>http://jabber.org/protocol/ibb</namespace>
|
||||
<className>org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider$IQProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<iqProvider>
|
||||
<elementName>close</elementName>
|
||||
<namespace>http://jabber.org/protocol/ibb</namespace>
|
||||
<className>org.jivesoftware.smackx.bytestreams.ibb.provider.CloseIQProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>data</elementName>
|
||||
<namespace>http://jabber.org/protocol/ibb</namespace>
|
||||
<className>org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider$PacketExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- Ad-Hoc Command -->
|
||||
<!-- XEP-0049: Private Data Storage -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:private</namespace>
|
||||
<className>org.jivesoftware.smackx.iqprivate.PrivateDataManager$PrivateDataIQProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0050: Ad-Hoc Commands -->
|
||||
<iqProvider>
|
||||
<elementName>command</elementName>
|
||||
<namespace>http://jabber.org/protocol/commands</namespace>
|
||||
<className>org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>bad-action</elementName>
|
||||
<namespace>http://jabber.org/protocol/commands</namespace>
|
||||
<className>org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$BadActionError</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>malformed-actionn</elementName>
|
||||
<namespace>http://jabber.org/protocol/commands</namespace>
|
||||
<className>org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$MalformedActionError</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>bad-locale</elementName>
|
||||
<namespace>http://jabber.org/protocol/commands</namespace>
|
||||
<className>org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$BadLocaleError</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>bad-payload</elementName>
|
||||
<namespace>http://jabber.org/protocol/commands</namespace>
|
||||
<className>org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$BadPayloadError</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>bad-sessionid</elementName>
|
||||
<namespace>http://jabber.org/protocol/commands</namespace>
|
||||
<className>org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$BadSessionIDError</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>session-expired</elementName>
|
||||
<namespace>http://jabber.org/protocol/commands</namespace>
|
||||
<className>org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider$SessionExpiredError</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- SHIM -->
|
||||
<!-- XEP-0054: VCard-Temp -->
|
||||
<iqProvider>
|
||||
<elementName>vCard</elementName>
|
||||
<namespace>vcard-temp</namespace>
|
||||
<className>org.jivesoftware.smackx.vcardtemp.provider.VCardProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0055: Jabber Search -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:search</namespace>
|
||||
<className>org.jivesoftware.smackx.search.UserSearch$Provider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0059: Result Set Management (RSM) -->
|
||||
<extensionProvider>
|
||||
<elementName>headers</elementName>
|
||||
<namespace>http://jabber.org/protocol/shim</namespace>
|
||||
<className>org.jivesoftware.smackx.shim.provider.HeadersProvider</className>
|
||||
<elementName>set</elementName>
|
||||
<namespace>http://jabber.org/protocol/rsm</namespace>
|
||||
<className>org.jivesoftware.smackx.rsm.provider.RSMSetProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>header</elementName>
|
||||
<namespace>http://jabber.org/protocol/shim</namespace>
|
||||
<className>org.jivesoftware.smackx.shim.provider.HeaderProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0060 pubsub -->
|
||||
<!-- XEP-0060: Publish-Subscribe -->
|
||||
<iqProvider>
|
||||
<elementName>pubsub</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.PubSubProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>create</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>items</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.ItemsProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>item</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.ItemProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>subscriptions</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.SubscriptionsProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>subscription</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>affiliations</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.AffiliationsProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>affiliation</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.AffiliationProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>options</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.FormNodeProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0060 pubsub#owner -->
|
||||
|
||||
<!-- XEP-0060: pubsub#owner -->
|
||||
<extensionProvider>
|
||||
<elementName>affiliation</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#owner</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.AffiliationProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<iqProvider>
|
||||
<elementName>pubsub</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#owner</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.PubSubProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>configure</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#owner</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.FormNodeProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>default</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#owner</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.FormNodeProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>subscriptions</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#owner</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.SubscriptionsProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>subscription</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#owner</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0060 pubsub#event -->
|
||||
<!-- XEP-0060: pubsub#event -->
|
||||
<extensionProvider>
|
||||
<elementName>event</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.EventProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>configuration</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.ConfigEventProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>delete</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>options</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.FormNodeProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>items</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.ItemsProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>item</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.ItemProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>retract</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.RetractEventProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>purge</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub#event</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- Nick Exchange -->
|
||||
<!-- XEP-0065: SOCKS5 Bytestreams -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>http://jabber.org/protocol/bytestreams</namespace>
|
||||
<className>org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0071: XHTML-IM -->
|
||||
<extensionProvider>
|
||||
<elementName>html</elementName>
|
||||
<namespace>http://jabber.org/protocol/xhtml-im</namespace>
|
||||
<className>org.jivesoftware.smackx.xhtmlim.provider.XHTMLExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0077: In-Band Registration -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:register</namespace>
|
||||
<className>org.jivesoftware.smackx.iqregister.provider.RegistrationProvider</className>
|
||||
</iqProvider>
|
||||
<streamFeatureProvider>
|
||||
<elementName>register</elementName>
|
||||
<namespace>http://jabber.org/features/iq-register</namespace>
|
||||
<className>org.jivesoftware.smackx.iqregister.provider.RegistrationStreamFeatureProvider</className>
|
||||
</streamFeatureProvider>
|
||||
|
||||
<!-- XEP-0079: Advanced Message Processing -->
|
||||
<extensionProvider>
|
||||
<elementName>amp</elementName>
|
||||
<namespace>http://jabber.org/protocol/amp</namespace>
|
||||
<className>org.jivesoftware.smackx.amp.provider.AMPExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0080: User Location -->
|
||||
<extensionProvider>
|
||||
<elementName>geoloc</elementName>
|
||||
<namespace>http://jabber.org/protocol/geoloc</namespace>
|
||||
<className>org.jivesoftware.smackx.geoloc.provider.GeoLocationProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0085: Chat State Notifications -->
|
||||
<extensionProvider>
|
||||
<elementName>active</elementName>
|
||||
<namespace>http://jabber.org/protocol/chatstates</namespace>
|
||||
<className>org.jivesoftware.smackx.chatstates.provider.ChatStateExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
<extensionProvider>
|
||||
<elementName>composing</elementName>
|
||||
<namespace>http://jabber.org/protocol/chatstates</namespace>
|
||||
<className>org.jivesoftware.smackx.chatstates.provider.ChatStateExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
<extensionProvider>
|
||||
<elementName>paused</elementName>
|
||||
<namespace>http://jabber.org/protocol/chatstates</namespace>
|
||||
<className>org.jivesoftware.smackx.chatstates.provider.ChatStateExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
<extensionProvider>
|
||||
<elementName>inactive</elementName>
|
||||
<namespace>http://jabber.org/protocol/chatstates</namespace>
|
||||
<className>org.jivesoftware.smackx.chatstates.provider.ChatStateExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
<extensionProvider>
|
||||
<elementName>gone</elementName>
|
||||
<namespace>http://jabber.org/protocol/chatstates</namespace>
|
||||
<className>org.jivesoftware.smackx.chatstates.provider.ChatStateExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0091: Legacy Delayed Delivery -->
|
||||
<extensionProvider>
|
||||
<elementName>x</elementName>
|
||||
<namespace>jabber:x:delay</namespace>
|
||||
<className>org.jivesoftware.smackx.delay.provider.LegacyDelayInformationProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0092: Software Version -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:version</namespace>
|
||||
<className>org.jivesoftware.smackx.iqversion.provider.VersionProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0096: SI File Transfer -->
|
||||
<iqProvider>
|
||||
<elementName>si</elementName>
|
||||
<namespace>http://jabber.org/protocol/si</namespace>
|
||||
<className>org.jivesoftware.smackx.si.provider.StreamInitiationProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0107: User Mood -->
|
||||
<extensionProvider>
|
||||
<elementName>mood</elementName>
|
||||
<namespace>http://jabber.org/protocol/mood</namespace>
|
||||
<className>org.jivesoftware.smackx.mood.provider.MoodProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0115: Entity Capabilities -->
|
||||
<extensionProvider>
|
||||
<elementName>c</elementName>
|
||||
<namespace>http://jabber.org/protocol/caps</namespace>
|
||||
<className>org.jivesoftware.smackx.caps.provider.CapsExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
<streamFeatureProvider>
|
||||
<elementName>c</elementName>
|
||||
<namespace>http://jabber.org/protocol/caps</namespace>
|
||||
<className>org.jivesoftware.smackx.caps.provider.CapsExtensionProvider</className>
|
||||
</streamFeatureProvider>
|
||||
|
||||
<!-- XEP-0118: User Tune -->
|
||||
<extensionProvider>
|
||||
<elementName>tune</elementName>
|
||||
<namespace>http://jabber.org/protocol/tune</namespace>
|
||||
<className>org.jivesoftware.smackx.usertune.provider.UserTuneProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0131: Stanza Headers and Internet Metadata (SHIM) -->
|
||||
<extensionProvider>
|
||||
<elementName>headers</elementName>
|
||||
<namespace>http://jabber.org/protocol/shim</namespace>
|
||||
<className>org.jivesoftware.smackx.shim.provider.HeadersProvider</className>
|
||||
</extensionProvider>
|
||||
<extensionProvider>
|
||||
<elementName>header</elementName>
|
||||
<namespace>http://jabber.org/protocol/shim</namespace>
|
||||
<className>org.jivesoftware.smackx.shim.provider.HeaderProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0166: Jingle -->
|
||||
<iqProvider>
|
||||
<elementName>jingle</elementName>
|
||||
<namespace>urn:xmpp:jingle:1</namespace>
|
||||
<className>org.jivesoftware.smackx.jingle.provider.JingleProvider</className>
|
||||
</iqProvider>
|
||||
<extensionProvider>
|
||||
<elementName>out-of-order</elementName>
|
||||
<namespace>urn:xmpp:jingle:errors:1</namespace>
|
||||
<className>org.jivesoftware.smackx.jingle.provider.JingleErrorProvider</className>
|
||||
</extensionProvider>
|
||||
<extensionProvider>
|
||||
<elementName>unknown-session</elementName>
|
||||
<namespace>urn:xmpp:jingle:errors:1</namespace>
|
||||
<className>org.jivesoftware.smackx.jingle.provider.JingleErrorProvider</className>
|
||||
</extensionProvider>
|
||||
<extensionProvider>
|
||||
<elementName>unsupported-content</elementName>
|
||||
<namespace>urn:xmpp:jingle:errors:1</namespace>
|
||||
<className>org.jivesoftware.smackx.jingle.provider.JingleErrorProvider</className>
|
||||
</extensionProvider>
|
||||
<extensionProvider>
|
||||
<elementName>unsupported-transports</elementName>
|
||||
<namespace>urn:xmpp:jingle:errors:1</namespace>
|
||||
<className>org.jivesoftware.smackx.jingle.provider.JingleErrorProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0172: User Nickname -->
|
||||
<extensionProvider>
|
||||
<elementName>nick</elementName>
|
||||
<namespace>http://jabber.org/protocol/nick</namespace>
|
||||
<className>org.jivesoftware.smackx.nick.provider.NickProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- Attention -->
|
||||
<extensionProvider>
|
||||
<elementName>attention</elementName>
|
||||
<namespace>urn:xmpp:attention:0</namespace>
|
||||
<className>org.jivesoftware.smackx.attention.packet.AttentionExtension$Provider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0184 Message Delivery Receipts -->
|
||||
<!-- XEP-0184: Message Delivery Receipts -->
|
||||
<extensionProvider>
|
||||
<elementName>received</elementName>
|
||||
<namespace>urn:xmpp:receipts</namespace>
|
||||
|
@ -433,96 +449,7 @@
|
|||
<className>org.jivesoftware.smackx.receipts.DeliveryReceiptRequest$Provider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0115 Entity Capabilities -->
|
||||
<extensionProvider>
|
||||
<elementName>c</elementName>
|
||||
<namespace>http://jabber.org/protocol/caps</namespace>
|
||||
<className>org.jivesoftware.smackx.caps.provider.CapsExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<streamFeatureProvider>
|
||||
<elementName>c</elementName>
|
||||
<namespace>http://jabber.org/protocol/caps</namespace>
|
||||
<className>org.jivesoftware.smackx.caps.provider.CapsExtensionProvider</className>
|
||||
</streamFeatureProvider>
|
||||
|
||||
<!-- XEP-0297 Stanza Forwarding -->
|
||||
<extensionProvider>
|
||||
<elementName>forwarded</elementName>
|
||||
<namespace>urn:xmpp:forward:0</namespace>
|
||||
<className>org.jivesoftware.smackx.forward.provider.ForwardedProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- Ping (XEP-199) Manager -->
|
||||
<iqProvider>
|
||||
<elementName>ping</elementName>
|
||||
<namespace>urn:xmpp:ping</namespace>
|
||||
<className>org.jivesoftware.smackx.ping.provider.PingProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- Privacy -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:privacy</namespace>
|
||||
<className>org.jivesoftware.smackx.privacy.provider.PrivacyProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0079 Advanced Message Processing -->
|
||||
<extensionProvider>
|
||||
<elementName>amp</elementName>
|
||||
<namespace>http://jabber.org/protocol/amp</namespace>
|
||||
<className>org.jivesoftware.smackx.amp.provider.AMPExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- JiveProperties -->
|
||||
<extensionProvider>
|
||||
<elementName>properties</elementName>
|
||||
<namespace>http://www.jivesoftware.com/xmlns/xmpp/properties</namespace>
|
||||
<className>org.jivesoftware.smackx.jiveproperties.provider.JivePropertiesExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0077: In-Band Registration -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:register</namespace>
|
||||
<className>org.jivesoftware.smackx.iqregister.provider.RegistrationProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<streamFeatureProvider>
|
||||
<elementName>register</elementName>
|
||||
<namespace>http://jabber.org/features/iq-register</namespace>
|
||||
<className>org.jivesoftware.smackx.iqregister.provider.RegistrationStreamFeatureProvider</className>
|
||||
</streamFeatureProvider>
|
||||
|
||||
<!-- XEP-0059: Result Set Management -->
|
||||
<extensionProvider>
|
||||
<elementName>set</elementName>
|
||||
<namespace>http://jabber.org/protocol/rsm</namespace>
|
||||
<className>org.jivesoftware.smackx.rsm.provider.RSMSetProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0080: User Location -->
|
||||
<extensionProvider>
|
||||
<elementName>geoloc</elementName>
|
||||
<namespace>http://jabber.org/protocol/geoloc</namespace>
|
||||
<className>org.jivesoftware.smackx.geoloc.provider.GeoLocationProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0308: Last Message Correction -->
|
||||
<extensionProvider>
|
||||
<elementName>replace</elementName>
|
||||
<namespace>urn:xmpp:message-correct:0</namespace>
|
||||
<className>org.jivesoftware.smackx.message_correct.provider.MessageCorrectProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0319: Last User Interaction in Presence -->
|
||||
<extensionProvider>
|
||||
<elementName>idle</elementName>
|
||||
<namespace>urn:xmpp:idle:1</namespace>
|
||||
<className>org.jivesoftware.smackx.last_interaction.provider.IdleProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0191 Blocking Command -->
|
||||
<!-- XEP-0191: Blocking Command -->
|
||||
<iqProvider>
|
||||
<elementName>blocklist</elementName>
|
||||
<namespace>urn:xmpp:blocking</namespace>
|
||||
|
@ -544,6 +471,34 @@
|
|||
<className>org.jivesoftware.smackx.blocking.provider.BlockedErrorExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0199: XMPP Ping -->
|
||||
<iqProvider>
|
||||
<elementName>ping</elementName>
|
||||
<namespace>urn:xmpp:ping</namespace>
|
||||
<className>org.jivesoftware.smackx.ping.provider.PingProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0202: Entity Time -->
|
||||
<iqProvider>
|
||||
<elementName>time</elementName>
|
||||
<namespace>urn:xmpp:time</namespace>
|
||||
<className>org.jivesoftware.smackx.time.provider.TimeProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0203: Delayed Delivery -->
|
||||
<extensionProvider>
|
||||
<elementName>delay</elementName>
|
||||
<namespace>urn:xmpp:delay</namespace>
|
||||
<className>org.jivesoftware.smackx.delay.provider.DelayInformationProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0224: Attention -->
|
||||
<extensionProvider>
|
||||
<elementName>attention</elementName>
|
||||
<namespace>urn:xmpp:attention:0</namespace>
|
||||
<className>org.jivesoftware.smackx.attention.packet.AttentionExtension$Provider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0231: Bits of Binary -->
|
||||
<iqProvider>
|
||||
<elementName>data</elementName>
|
||||
|
@ -551,49 +506,46 @@
|
|||
<className>org.jivesoftware.smackx.bob.provider.BoBIQProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- XEP-0166: Jingle -->
|
||||
<!-- XEP-0249: Group Chat Invitations -->
|
||||
<extensionProvider>
|
||||
<elementName>x</elementName>
|
||||
<namespace>jabber:x:conference</namespace>
|
||||
<className>org.jivesoftware.smackx.muc.packet.GroupChatInvitation$Provider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0297: Stanza Forwarding -->
|
||||
<extensionProvider>
|
||||
<elementName>forwarded</elementName>
|
||||
<namespace>urn:xmpp:forward:0</namespace>
|
||||
<className>org.jivesoftware.smackx.forward.provider.ForwardedProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0308: Last Message Correction -->
|
||||
<extensionProvider>
|
||||
<elementName>replace</elementName>
|
||||
<namespace>urn:xmpp:message-correct:0</namespace>
|
||||
<className>org.jivesoftware.smackx.message_correct.provider.MessageCorrectProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0319: Last User Interaction in Presence -->
|
||||
<extensionProvider>
|
||||
<elementName>idle</elementName>
|
||||
<namespace>urn:xmpp:idle:1</namespace>
|
||||
<className>org.jivesoftware.smackx.last_interaction.provider.IdleProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- JiveProperties -->
|
||||
<extensionProvider>
|
||||
<elementName>properties</elementName>
|
||||
<namespace>http://www.jivesoftware.com/xmlns/xmpp/properties</namespace>
|
||||
<className>org.jivesoftware.smackx.jiveproperties.provider.JivePropertiesExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- SharedGroupsInfo -->
|
||||
<iqProvider>
|
||||
<elementName>jingle</elementName>
|
||||
<namespace>urn:xmpp:jingle:1</namespace>
|
||||
<className>org.jivesoftware.smackx.jingle.provider.JingleProvider</className>
|
||||
<elementName>sharedgroup</elementName>
|
||||
<namespace>http://www.jivesoftware.org/protocol/sharedgroup</namespace>
|
||||
<className>org.jivesoftware.smackx.sharedgroups.packet.SharedGroupsInfo$Provider</className>
|
||||
</iqProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>out-of-order</elementName>
|
||||
<namespace>urn:xmpp:jingle:errors:1</namespace>
|
||||
<className>org.jivesoftware.smackx.jingle.provider.JingleErrorProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>unknown-session</elementName>
|
||||
<namespace>urn:xmpp:jingle:errors:1</namespace>
|
||||
<className>org.jivesoftware.smackx.jingle.provider.JingleErrorProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>unsupported-content</elementName>
|
||||
<namespace>urn:xmpp:jingle:errors:1</namespace>
|
||||
<className>org.jivesoftware.smackx.jingle.provider.JingleErrorProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>unsupported-transports</elementName>
|
||||
<namespace>urn:xmpp:jingle:errors:1</namespace>
|
||||
<className>org.jivesoftware.smackx.jingle.provider.JingleErrorProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0107: User Mood -->
|
||||
<extensionProvider>
|
||||
<elementName>mood</elementName>
|
||||
<namespace>http://jabber.org/protocol/mood</namespace>
|
||||
<className>org.jivesoftware.smackx.mood.provider.MoodProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0118: User Tune -->
|
||||
<extensionProvider>
|
||||
<elementName>tune</elementName>
|
||||
<namespace>http://jabber.org/protocol/tune</namespace>
|
||||
<className>org.jivesoftware.smackx.usertune.provider.UserTuneProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
</smackProviders>
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
<?xml version="1.0"?>
|
||||
<smackProviders>
|
||||
|
||||
<!-- RFC-6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:roster</namespace>
|
||||
<className>org.jivesoftware.smack.roster.provider.RosterPacketProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<streamFeatureProvider>
|
||||
<elementName>sub</elementName>
|
||||
<namespace>urn:xmpp:features:pre-approval</namespace>
|
||||
<className>org.jivesoftware.smack.roster.provider.SubscriptionPreApprovalStreamFeatureProvider</className>
|
||||
</streamFeatureProvider>
|
||||
|
||||
<!-- XEP-0237: Roster Versioning -->
|
||||
<streamFeatureProvider>
|
||||
<elementName>ver</elementName>
|
||||
<namespace>urn:xmpp:features:rosterver</namespace>
|
||||
|
|
|
@ -7,13 +7,7 @@ mainClassName = 'org.igniterealtime.smack.inttest.SmackIntegrationTestFramework'
|
|||
applicationDefaultJvmArgs = ["-enableassertions"]
|
||||
|
||||
dependencies {
|
||||
compile project(':smack-java7')
|
||||
compile project(':smack-tcp')
|
||||
compile project(':smack-extensions')
|
||||
compile project(':smack-experimental')
|
||||
compile project(':smack-omemo')
|
||||
compile project(':smack-openpgp')
|
||||
compile project(':smack-debug')
|
||||
api project(':smack-java8-full')
|
||||
compile 'org.reflections:reflections:0.9.11'
|
||||
compile 'eu.geekplace.javapinning:java-pinning-java7:1.1.0-alpha1'
|
||||
compile group: 'commons-io', name: 'commons-io', version: "$commonsIoVersion"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2019 Florian Schmaus
|
||||
* Copyright 2015-2020 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -45,7 +45,7 @@ public abstract class AbstractSmackIntTest {
|
|||
|
||||
protected final Configuration sinttestConfiguration;
|
||||
|
||||
protected AbstractSmackIntTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
protected AbstractSmackIntTest(SmackIntegrationTestEnvironment environment) {
|
||||
this.testRunId = environment.testRunId;
|
||||
this.sinttestConfiguration = environment.configuration;
|
||||
this.timeout = environment.configuration.replyTimeout;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2018 Florian Schmaus
|
||||
* Copyright 2015-2020 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,7 +46,7 @@ public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest
|
|||
|
||||
protected final List<XMPPConnection> connections;
|
||||
|
||||
public AbstractSmackIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public AbstractSmackIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
this.connection = this.conOne = environment.conOne;
|
||||
this.conTwo = environment.conTwo;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2019 Florian Schmaus
|
||||
* Copyright 2015-2020 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -31,7 +31,7 @@ import org.jxmpp.jid.DomainBareJid;
|
|||
|
||||
public abstract class AbstractSmackLowLevelIntegrationTest extends AbstractSmackIntTest {
|
||||
|
||||
private final SmackIntegrationTestEnvironment<?> environment;
|
||||
private final SmackIntegrationTestEnvironment environment;
|
||||
|
||||
/**
|
||||
* The configuration
|
||||
|
@ -40,7 +40,7 @@ public abstract class AbstractSmackLowLevelIntegrationTest extends AbstractSmack
|
|||
|
||||
protected final DomainBareJid service;
|
||||
|
||||
protected AbstractSmackLowLevelIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
protected AbstractSmackLowLevelIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
this.environment = environment;
|
||||
this.configuration = environment.configuration;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2019 Florian Schmaus
|
||||
* Copyright 2018-2020 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -28,19 +28,22 @@ import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
|||
public abstract class AbstractSmackSpecificLowLevelIntegrationTest<C extends AbstractXMPPConnection>
|
||||
extends AbstractSmackLowLevelIntegrationTest {
|
||||
|
||||
private final SmackIntegrationTestEnvironment<?> environment;
|
||||
private final SmackIntegrationTestEnvironment environment;
|
||||
|
||||
protected final Class<C> connectionClass;
|
||||
|
||||
private final XmppConnectionDescriptor<C, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>> connectionDescriptor;
|
||||
|
||||
public AbstractSmackSpecificLowLevelIntegrationTest(SmackIntegrationTestEnvironment<?> environment,
|
||||
public AbstractSmackSpecificLowLevelIntegrationTest(SmackIntegrationTestEnvironment environment,
|
||||
Class<C> connectionClass) {
|
||||
super(environment);
|
||||
this.environment = environment;
|
||||
this.connectionClass = connectionClass;
|
||||
|
||||
connectionDescriptor = environment.connectionManager.getConnectionDescriptorFor(connectionClass);
|
||||
if (connectionDescriptor == null) {
|
||||
throw new IllegalStateException("No connection descriptor for " + connectionClass + " known");
|
||||
}
|
||||
}
|
||||
|
||||
public Class<C> getConnectionClass() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2018 Florian Schmaus
|
||||
* Copyright 2015-2020 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,9 @@ import javax.net.ssl.SSLContext;
|
|||
|
||||
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
|
||||
import org.jivesoftware.smack.debugger.ConsoleDebugger;
|
||||
import org.jivesoftware.smack.util.Function;
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jivesoftware.smack.util.ParserUtils;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
import org.jivesoftware.smackx.debugger.EnhancedDebugger;
|
||||
|
@ -94,80 +96,90 @@ public final class Configuration {
|
|||
|
||||
public final Set<String> disabledTests;
|
||||
|
||||
public final String defaultConnectionNickname;
|
||||
|
||||
public final Set<String> enabledConnections;
|
||||
|
||||
public final Set<String> disabledConnections;
|
||||
|
||||
public final Set<String> testPackages;
|
||||
|
||||
public final ConnectionConfigurationBuilderApplier configurationApplier;
|
||||
|
||||
private Configuration(DomainBareJid service, String serviceTlsPin, SecurityMode securityMode, int replyTimeout,
|
||||
Debugger debugger, String accountOneUsername, String accountOnePassword, String accountTwoUsername,
|
||||
String accountTwoPassword, String accountThreeUsername, String accountThreePassword, Set<String> enabledTests, Set<String> disabledTests,
|
||||
Set<String> testPackages, String adminAccountUsername, String adminAccountPassword)
|
||||
throws KeyManagementException, NoSuchAlgorithmException {
|
||||
this.service = Objects.requireNonNull(service,
|
||||
public final boolean verbose;
|
||||
|
||||
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'.");
|
||||
this.serviceTlsPin = serviceTlsPin;
|
||||
serviceTlsPin = builder.serviceTlsPin;
|
||||
if (serviceTlsPin != null) {
|
||||
tlsContext = Java7Pinning.forPin(serviceTlsPin);
|
||||
} else {
|
||||
tlsContext = null;
|
||||
}
|
||||
this.securityMode = securityMode;
|
||||
if (replyTimeout > 0) {
|
||||
this.replyTimeout = replyTimeout;
|
||||
securityMode = builder.securityMode;
|
||||
if (builder.replyTimeout > 0) {
|
||||
replyTimeout = builder.replyTimeout;
|
||||
} else {
|
||||
this.replyTimeout = 60000;
|
||||
replyTimeout = 60000;
|
||||
}
|
||||
this.debugger = debugger;
|
||||
if (StringUtils.isNotEmpty(adminAccountUsername, adminAccountPassword)) {
|
||||
debugger = builder.debugger;
|
||||
if (StringUtils.isNotEmpty(builder.adminAccountUsername, builder.adminAccountPassword)) {
|
||||
accountRegistration = AccountRegistration.serviceAdministration;
|
||||
}
|
||||
else if (StringUtils.isNotEmpty(accountOneUsername, accountOnePassword, accountTwoUsername, accountTwoPassword,
|
||||
accountThreeUsername, accountThreePassword)) {
|
||||
else if (StringUtils.isNotEmpty(builder.accountOneUsername, builder.accountOnePassword,
|
||||
builder.accountTwoUsername, builder.accountTwoPassword, builder.accountThreeUsername,
|
||||
builder.accountThreePassword)) {
|
||||
accountRegistration = AccountRegistration.disabled;
|
||||
}
|
||||
else {
|
||||
accountRegistration = AccountRegistration.inBandRegistration;
|
||||
}
|
||||
|
||||
this.adminAccountUsername = adminAccountUsername;
|
||||
this.adminAccountPassword = adminAccountPassword;
|
||||
this.adminAccountUsername = builder.adminAccountUsername;
|
||||
this.adminAccountPassword = builder.adminAccountPassword;
|
||||
|
||||
boolean accountOnePasswordSet = StringUtils.isNotEmpty(accountOnePassword);
|
||||
if (accountOnePasswordSet != StringUtils.isNotEmpty(accountTwoPassword) ||
|
||||
accountOnePasswordSet != StringUtils.isNotEmpty(accountThreePassword)) {
|
||||
boolean accountOnePasswordSet = StringUtils.isNotEmpty(builder.accountOnePassword);
|
||||
if (accountOnePasswordSet != StringUtils.isNotEmpty(builder.accountTwoPassword) ||
|
||||
accountOnePasswordSet != StringUtils.isNotEmpty(builder.accountThreePassword)) {
|
||||
// Ensure the invariant that either all main accounts have a password set, or none.
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
this.accountOneUsername = accountOneUsername;
|
||||
this.accountOnePassword = accountOnePassword;
|
||||
this.accountTwoUsername = accountTwoUsername;
|
||||
this.accountTwoPassword = accountTwoPassword;
|
||||
this.accountThreeUsername = accountThreeUsername;
|
||||
this.accountThreePassword = accountThreePassword;
|
||||
this.enabledTests = enabledTests;
|
||||
this.disabledTests = disabledTests;
|
||||
this.testPackages = testPackages;
|
||||
this.accountOneUsername = builder.accountOneUsername;
|
||||
this.accountOnePassword = builder.accountOnePassword;
|
||||
this.accountTwoUsername = builder.accountTwoUsername;
|
||||
this.accountTwoPassword = builder.accountTwoPassword;
|
||||
this.accountThreeUsername = builder.accountThreeUsername;
|
||||
this.accountThreePassword = builder.accountThreePassword;
|
||||
this.enabledTests = builder.enabledTests;
|
||||
this.disabledTests = builder.disabledTests;
|
||||
this.defaultConnectionNickname = builder.defaultConnectionNickname;
|
||||
this.enabledConnections = builder.enabledConnections;
|
||||
this.disabledConnections = builder.disabledConnections;
|
||||
this.testPackages = builder.testPackages;
|
||||
|
||||
this.configurationApplier = builder -> {
|
||||
this.configurationApplier = b -> {
|
||||
if (tlsContext != null) {
|
||||
builder.setCustomSSLContext(tlsContext);
|
||||
b.setCustomSSLContext(tlsContext);
|
||||
}
|
||||
builder.setSecurityMode(securityMode);
|
||||
builder.setXmppDomain(service);
|
||||
b.setSecurityMode(securityMode);
|
||||
b.setXmppDomain(service);
|
||||
|
||||
switch (debugger) {
|
||||
case enhanced:
|
||||
builder.setDebuggerFactory(EnhancedDebugger.Factory.INSTANCE);
|
||||
b.setDebuggerFactory(EnhancedDebugger.Factory.INSTANCE);
|
||||
break;
|
||||
case console:
|
||||
builder.setDebuggerFactory(ConsoleDebugger.Factory.INSTANCE);
|
||||
b.setDebuggerFactory(ConsoleDebugger.Factory.INSTANCE);
|
||||
break;
|
||||
case none:
|
||||
// Nothing to do :).
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
this.verbose = builder.verbose;
|
||||
}
|
||||
|
||||
public boolean isAccountRegistrationPossible() {
|
||||
|
@ -210,8 +222,16 @@ public final class Configuration {
|
|||
|
||||
private Set<String> disabledTests;
|
||||
|
||||
private String defaultConnectionNickname;
|
||||
|
||||
private Set<String> enabledConnections;
|
||||
|
||||
private Set<String> disabledConnections;
|
||||
|
||||
private Set<String> testPackages;
|
||||
|
||||
private boolean verbose;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
|
@ -324,6 +344,21 @@ public final class Configuration {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder setDefaultConnection(String defaultConnectionNickname) {
|
||||
this.defaultConnectionNickname = defaultConnectionNickname;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setEnabledConnections(String enabledConnectionsString) {
|
||||
enabledConnections = split(enabledConnectionsString);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDisabledConnections(String disabledConnectionsString) {
|
||||
disabledConnections = split(disabledConnectionsString);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addTestPackages(String testPackagesString) {
|
||||
if (testPackagesString != null) {
|
||||
String[] testPackagesArray = testPackagesString.split(",");
|
||||
|
@ -350,10 +385,22 @@ public final class Configuration {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder setVerbose(boolean verbose) {
|
||||
this.verbose = verbose;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setVerbose(String verboseBooleanString) {
|
||||
if (verboseBooleanString == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
boolean verbose = ParserUtils.parseXmlBoolean(verboseBooleanString);
|
||||
return setVerbose(verbose);
|
||||
}
|
||||
|
||||
public Configuration build() throws KeyManagementException, NoSuchAlgorithmException {
|
||||
return new Configuration(service, serviceTlsPin, securityMode, replyTimeout, debugger, accountOneUsername,
|
||||
accountOnePassword, accountTwoUsername, accountTwoPassword, accountThreeUsername, accountThreePassword, enabledTests, disabledTests,
|
||||
testPackages, adminAccountUsername, adminAccountPassword);
|
||||
return new Configuration(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -414,10 +461,15 @@ public final class Configuration {
|
|||
builder.setDebugger(properties.getProperty("debugger"));
|
||||
builder.setEnabledTests(properties.getProperty("enabledTests"));
|
||||
builder.setDisabledTests(properties.getProperty("disabledTests"));
|
||||
builder.setDefaultConnection(properties.getProperty("defaultConnection"));
|
||||
builder.setEnabledConnections(properties.getProperty("enabledConnections"));
|
||||
builder.setDisabledConnections(properties.getProperty("disabledConnections"));
|
||||
|
||||
builder.addTestPackages(properties.getProperty("testPackages"));
|
||||
builder.addTestPackages(testPackages);
|
||||
|
||||
builder.setVerbose(properties.getProperty("verbose"));
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
@ -437,23 +489,36 @@ public final class Configuration {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static Set<String> getTestSetFrom(String string) {
|
||||
if (string == null) {
|
||||
private static Set<String> split(String input) {
|
||||
return split(input, Function.identity());
|
||||
}
|
||||
|
||||
private static Set<String> split(String input, Function<String, String> transformer) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
String[] stringArray = string.split(",");
|
||||
Set<String> res = new HashSet<>(stringArray.length);
|
||||
for (String s : stringArray) {
|
||||
res.add(getFullTestStringFrom(s));
|
||||
|
||||
String[] inputArray = input.split(",");
|
||||
Set<String> res = new HashSet<>(inputArray.length);
|
||||
for (String s : inputArray) {
|
||||
s = transformer.apply(s);
|
||||
boolean newElement = res.add(s);
|
||||
if (!newElement) {
|
||||
throw new IllegalArgumentException("The argument '" + s + "' was already provided.");
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static String getFullTestStringFrom(String string) {
|
||||
string = string.trim();
|
||||
if (string.startsWith("smackx.") || string.startsWith("smack.")) {
|
||||
string = "org.jivesoftware." + string;
|
||||
private static Set<String> getTestSetFrom(String input) {
|
||||
return split(input, s -> {
|
||||
s = s.trim();
|
||||
if (s.startsWith("smackx.") || s.startsWith("smack.")) {
|
||||
s = "org.jivesoftware." + s;
|
||||
}
|
||||
return string;
|
||||
return s;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2019 Florian Schmaus
|
||||
* Copyright 2015-2020 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,18 @@ package org.igniterealtime.smack.inttest;
|
|||
|
||||
import org.jivesoftware.smack.AbstractXMPPConnection;
|
||||
|
||||
public class SmackIntegrationTestEnvironment<C extends AbstractXMPPConnection> {
|
||||
public class SmackIntegrationTestEnvironment {
|
||||
|
||||
public final C conOne, conTwo, conThree;
|
||||
public final AbstractXMPPConnection conOne, conTwo, conThree;
|
||||
|
||||
public final String testRunId;
|
||||
|
||||
public final Configuration configuration;
|
||||
|
||||
public final XmppConnectionManager<C> connectionManager;
|
||||
public final XmppConnectionManager connectionManager;
|
||||
|
||||
SmackIntegrationTestEnvironment(C conOne, C conTwo, C conThree, String testRunId,
|
||||
Configuration configuration, XmppConnectionManager<C> connectionManager) {
|
||||
SmackIntegrationTestEnvironment(AbstractXMPPConnection conOne, AbstractXMPPConnection conTwo, AbstractXMPPConnection conThree, String testRunId,
|
||||
Configuration configuration, XmppConnectionManager connectionManager) {
|
||||
this.conOne = conOne;
|
||||
this.conTwo = conTwo;
|
||||
this.conThree = conThree;
|
||||
|
|
|
@ -23,6 +23,7 @@ import static org.reflections.ReflectionUtils.withParametersCount;
|
|||
import static org.reflections.ReflectionUtils.withReturnType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
@ -32,6 +33,7 @@ import java.lang.reflect.Type;
|
|||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -43,7 +45,6 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
@ -53,7 +54,6 @@ import org.jivesoftware.smack.SmackConfiguration;
|
|||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
|
||||
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.TLSUtils;
|
||||
|
@ -70,7 +70,7 @@ import org.reflections.scanners.MethodParameterScanner;
|
|||
import org.reflections.scanners.SubTypesScanner;
|
||||
import org.reflections.scanners.TypeAnnotationsScanner;
|
||||
|
||||
public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
||||
public class SmackIntegrationTestFramework {
|
||||
|
||||
static {
|
||||
TLSUtils.setDefaultTrustStoreTypeToJksIfRequired();
|
||||
|
@ -80,14 +80,12 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
|
||||
public static boolean SINTTEST_UNIT_TEST = false;
|
||||
|
||||
private final Class<DC> defaultConnectionClass;
|
||||
|
||||
protected final Configuration config;
|
||||
|
||||
protected TestRunResult testRunResult;
|
||||
|
||||
private SmackIntegrationTestEnvironment<DC> environment;
|
||||
protected XmppConnectionManager<DC> connectionManager;
|
||||
private SmackIntegrationTestEnvironment environment;
|
||||
protected XmppConnectionManager connectionManager;
|
||||
|
||||
public enum TestType {
|
||||
Normal,
|
||||
|
@ -100,7 +98,7 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||
Configuration config = Configuration.newConfiguration(args);
|
||||
|
||||
SmackIntegrationTestFramework<XMPPTCPConnection> sinttest = new SmackIntegrationTestFramework<>(config, XMPPTCPConnection.class);
|
||||
SmackIntegrationTestFramework sinttest = new SmackIntegrationTestFramework(config);
|
||||
TestRunResult testRunResult = sinttest.run();
|
||||
|
||||
for (Entry<Class<? extends AbstractSmackIntTest>, Throwable> entry : testRunResult.impossibleTestClasses.entrySet()) {
|
||||
|
@ -116,11 +114,9 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
}
|
||||
final int successfulTests = testRunResult.successfulIntegrationTests.size();
|
||||
final int failedTests = testRunResult.failedIntegrationTests.size();
|
||||
final int totalIntegrationTests = successfulTests + failedTests;
|
||||
final int availableTests = testRunResult.getNumberOfAvailableTests();
|
||||
final int possibleTests = testRunResult.getNumberOfPossibleTests();
|
||||
LOGGER.info("SmackIntegrationTestFramework[" + testRunResult.testRunId + ']' + ": Finished ["
|
||||
+ successfulTests + '/' + totalIntegrationTests + "] (" + possibleTests + " test methods of " + availableTests + " where possible)");
|
||||
LOGGER.info("SmackIntegrationTestFramework[" + testRunResult.testRunId + ']' + " finished: "
|
||||
+ successfulTests + '/' + availableTests + " [" + failedTests + " failed]");
|
||||
|
||||
final int exitStatus;
|
||||
if (failedTests > 0) {
|
||||
|
@ -146,12 +142,8 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
System.exit(exitStatus);
|
||||
}
|
||||
|
||||
public SmackIntegrationTestFramework(Configuration configuration, Class<DC> defaultConnectionClass)
|
||||
throws KeyManagementException, InstantiationException, IllegalAccessException, IllegalArgumentException,
|
||||
InvocationTargetException, NoSuchAlgorithmException, SmackException, IOException, XMPPException,
|
||||
InterruptedException {
|
||||
public SmackIntegrationTestFramework(Configuration configuration) {
|
||||
this.config = configuration;
|
||||
this.defaultConnectionClass = defaultConnectionClass;
|
||||
}
|
||||
|
||||
public synchronized TestRunResult run()
|
||||
|
@ -160,7 +152,7 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
testRunResult = new TestRunResult();
|
||||
|
||||
// Create a connection manager *after* we created the testRunId (in testRunResult).
|
||||
this.connectionManager = new XmppConnectionManager<>(this, defaultConnectionClass);
|
||||
this.connectionManager = new XmppConnectionManager(this);
|
||||
|
||||
LOGGER.info("SmackIntegrationTestFramework [" + testRunResult.testRunId + ']' + ": Starting");
|
||||
if (config.debugger != Configuration.Debugger.none) {
|
||||
|
@ -226,10 +218,13 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
return testRunResult;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "Finally"})
|
||||
@SuppressWarnings({"Finally"})
|
||||
private void runTests(Set<Class<? extends AbstractSmackIntTest>> classes)
|
||||
throws InterruptedException, InstantiationException, IllegalAccessException,
|
||||
IllegalArgumentException, SmackException, IOException, XMPPException {
|
||||
List<PreparedTest> tests = new ArrayList<>(classes.size());
|
||||
int numberOfAvailableTests = 0;
|
||||
|
||||
for (Class<? extends AbstractSmackIntTest> testClass : classes) {
|
||||
final String testClassName = testClass.getName();
|
||||
|
||||
|
@ -260,17 +255,19 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
// - https://discuss.gradle.org/t/main-vs-test-compile-vs-runtime-classpaths-in-eclipse-once-and-for-all-how/17403
|
||||
// - https://bugs.eclipse.org/bugs/show_bug.cgi?id=376616 (Scope of dependencies has no effect on Eclipse compilation)
|
||||
if (!SINTTEST_UNIT_TEST && testClassName.startsWith("org.igniterealtime.smack.inttest.unittest")) {
|
||||
LOGGER.finer("Skipping integration test '" + testClassName + "' from src/test classpath");
|
||||
LOGGER.warning("Skipping integration test '" + testClassName + "' from src/test classpath (should not be in classpath)");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.enabledTests != null && !isInSet(testClass, config.enabledTests)) {
|
||||
LOGGER.info("Skipping test class " + testClassName + " because it is not enabled");
|
||||
DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test class " + testClassName + " because it is not enabled");
|
||||
testRunResult.disabledTestClasses.add(disabledTestClass);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isInSet(testClass, config.disabledTests)) {
|
||||
LOGGER.info("Skipping test class " + testClassName + " because it is disalbed");
|
||||
DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test class " + testClassName + " because it is disalbed");
|
||||
testRunResult.disabledTestClasses.add(disabledTestClass);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -301,8 +298,6 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
continue;
|
||||
}
|
||||
|
||||
testRunResult.numberOfAvailableTestMethods.addAndGet(smackIntegrationTestMethods.size());
|
||||
|
||||
final AbstractSmackIntTest test;
|
||||
try {
|
||||
test = cons.newInstance(environment);
|
||||
|
@ -360,12 +355,14 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
final String methodName = method.getName();
|
||||
if (config.enabledTests != null && !(config.enabledTests.contains(methodName)
|
||||
|| isInSet(testClass, config.enabledTests))) {
|
||||
LOGGER.fine("Skipping test method " + methodName + " because it is not enabled");
|
||||
DisabledTest disabledTest = new DisabledTest(method, "Skipping test method " + methodName + " because it is not enabled");
|
||||
testRunResult.disabledTests.add(disabledTest);
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
if (config.disabledTests != null && config.disabledTests.contains(methodName)) {
|
||||
LOGGER.info("Skipping test method " + methodName + " because it is disabled");
|
||||
DisabledTest disabledTest = new DisabledTest(method, "Skipping test method " + methodName + " because it is disabled");
|
||||
testRunResult.disabledTests.add(disabledTest);
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
|
@ -376,47 +373,14 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
continue;
|
||||
}
|
||||
|
||||
final int detectedTestMethodsCount = smackIntegrationTestMethods.size();
|
||||
testRunResult.numberOfPossibleTestMethods.addAndGet(detectedTestMethodsCount);
|
||||
|
||||
try {
|
||||
// Run the @BeforeClass methods (if any)
|
||||
Set<Method> beforeClassMethods = getAllMethods(testClass,
|
||||
withAnnotation(BeforeClass.class), withReturnType(Void.TYPE),
|
||||
withParametersCount(0), withModifier(Modifier.PUBLIC
|
||||
));
|
||||
|
||||
// See if there are any methods that have the @BeforeClassAnnotation but a wrong signature
|
||||
Set<Method> allBeforeClassMethods = getAllMethods(testClass, withAnnotation(BeforeClass.class));
|
||||
allBeforeClassMethods.removeAll(beforeClassMethods);
|
||||
if (!allBeforeClassMethods.isEmpty()) {
|
||||
throw new IllegalArgumentException("@BeforeClass methods with wrong signature found");
|
||||
}
|
||||
|
||||
if (beforeClassMethods.size() == 1) {
|
||||
Method beforeClassMethod = beforeClassMethods.iterator().next();
|
||||
LOGGER.info("Executing @BeforeClass method of " + testClass);
|
||||
try {
|
||||
beforeClassMethod.invoke(test);
|
||||
}
|
||||
catch (InvocationTargetException | IllegalAccessException e) {
|
||||
LOGGER.log(Level.SEVERE, "Exception executing @BeforeClass method", e);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
else if (beforeClassMethods.size() > 1) {
|
||||
throw new IllegalArgumentException("Only one @BeforeClass method allowed");
|
||||
}
|
||||
List<ConcreteTest> concreteTests = new ArrayList<>(smackIntegrationTestMethods.size());
|
||||
|
||||
for (Method testMethod : smackIntegrationTestMethods) {
|
||||
List<ConcreteTest> concreteTests = null;
|
||||
switch (testType) {
|
||||
case Normal: {
|
||||
ConcreteTest.Executor concreteTestExecutor = () -> testMethod.invoke(test);
|
||||
ConcreteTest concreteTest = new ConcreteTest(testType, testMethod, concreteTestExecutor);
|
||||
concreteTests = Collections.singletonList(concreteTest);
|
||||
concreteTests.add(concreteTest);
|
||||
}
|
||||
break;
|
||||
case LowLevel:
|
||||
|
@ -424,13 +388,14 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
LowLevelTestMethod lowLevelTestMethod = new LowLevelTestMethod(testMethod);
|
||||
switch (testType) {
|
||||
case LowLevel:
|
||||
concreteTests = invokeLowLevel(lowLevelTestMethod, (AbstractSmackLowLevelIntegrationTest) test);
|
||||
List<ConcreteTest> concreteLowLevelTests = invokeLowLevel(lowLevelTestMethod, (AbstractSmackLowLevelIntegrationTest) test);
|
||||
concreteTests.addAll(concreteLowLevelTests);
|
||||
break;
|
||||
case SpecificLowLevel: {
|
||||
ConcreteTest.Executor concreteTestExecutor = () -> invokeSpecificLowLevel(
|
||||
lowLevelTestMethod, (AbstractSmackSpecificLowLevelIntegrationTest<?>) test);
|
||||
ConcreteTest concreteTest = new ConcreteTest(testType, testMethod, concreteTestExecutor);
|
||||
concreteTests = Collections.singletonList(concreteTest);
|
||||
concreteTests.add(concreteTest);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -438,44 +403,47 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (ConcreteTest concreteTest : concreteTests) {
|
||||
runConcreteTest(concreteTest);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
// Run the @AfterClass method (if any)
|
||||
Set<Method> afterClassMethods = getAllMethods(testClass,
|
||||
withAnnotation(AfterClass.class), withReturnType(Void.TYPE),
|
||||
withParametersCount(0), withModifier(Modifier.PUBLIC
|
||||
));
|
||||
|
||||
// See if there are any methods that have the @AfterClassAnnotation but a wrong signature
|
||||
Set<Method> allAfterClassMethods = getAllMethods(testClass, withAnnotation(AfterClass.class));
|
||||
allAfterClassMethods.removeAll(afterClassMethods);
|
||||
if (!allAfterClassMethods.isEmpty()) {
|
||||
throw new IllegalArgumentException("@AfterClass methods with wrong signature found");
|
||||
}
|
||||
|
||||
if (afterClassMethods.size() == 1) {
|
||||
Method afterClassMethod = afterClassMethods.iterator().next();
|
||||
LOGGER.info("Executing @AfterClass method of " + testClass);
|
||||
try {
|
||||
afterClassMethod.invoke(test);
|
||||
// Instantiate the prepared test early as this will check the before and after class annotations.
|
||||
PreparedTest preparedTest = new PreparedTest(test, concreteTests);
|
||||
tests.add(preparedTest);
|
||||
|
||||
numberOfAvailableTests += concreteTests.size();
|
||||
}
|
||||
catch (InvocationTargetException | IllegalAccessException e) {
|
||||
LOGGER.log(Level.SEVERE, "Exception executing @AfterClass method", e);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
throw new AssertionError(e);
|
||||
|
||||
// Print status information.
|
||||
StringBuilder sb = new StringBuilder(1024);
|
||||
sb.append("Smack Integration Test Framework\n");
|
||||
sb.append("################################\n");
|
||||
if (config.verbose) {
|
||||
sb.append('\n');
|
||||
if (!testRunResult.disabledTestClasses.isEmpty()) {
|
||||
sb.append("The following test classes are disabled:\n");
|
||||
for (DisabledTestClass disabledTestClass : testRunResult.disabledTestClasses) {
|
||||
disabledTestClass.appendTo(sb).append('\n');
|
||||
}
|
||||
}
|
||||
else if (afterClassMethods.size() > 1) {
|
||||
throw new IllegalArgumentException("Only one @AfterClass method allowed");
|
||||
if (!testRunResult.disabledTests.isEmpty()) {
|
||||
sb.append("The following tests are disabled:\n");
|
||||
for (DisabledTest disabledTest : testRunResult.disabledTests) {
|
||||
disabledTest.appendTo(sb).append('\n');
|
||||
}
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
sb.append("Available tests: ").append(numberOfAvailableTests)
|
||||
.append("(#-classes: ").append(testRunResult.disabledTestClasses.size())
|
||||
.append(", #-tests: ").append(testRunResult.disabledTests.size())
|
||||
.append(")\n");
|
||||
LOGGER.info(sb.toString());
|
||||
|
||||
for (PreparedTest test : tests) {
|
||||
test.run();
|
||||
}
|
||||
|
||||
// Assert that all tests in the 'tests' list produced a result.
|
||||
assert numberOfAvailableTests == testRunResult.getNumberOfAvailableTests();
|
||||
}
|
||||
|
||||
private void runConcreteTest(ConcreteTest concreteTest)
|
||||
|
@ -524,17 +492,35 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
}
|
||||
|
||||
private List<ConcreteTest> invokeLowLevel(LowLevelTestMethod lowLevelTestMethod, AbstractSmackLowLevelIntegrationTest test) {
|
||||
Set<Class<? extends AbstractXMPPConnection>> connectionClasses;
|
||||
Collection<? extends XmppConnectionDescriptor<?, ?, ?>> connectionDescriptors;
|
||||
if (lowLevelTestMethod.smackIntegrationTestAnnotation.onlyDefaultConnectionType()) {
|
||||
Class<? extends AbstractXMPPConnection> defaultConnectionClass = connectionManager.getDefaultConnectionClass();
|
||||
connectionClasses = Collections.singleton(defaultConnectionClass);
|
||||
XmppConnectionDescriptor<?, ?, ?> defaultConnectionDescriptor = connectionManager.getDefaultConnectionDescriptor();
|
||||
connectionDescriptors = Collections.singleton(defaultConnectionDescriptor);
|
||||
} else {
|
||||
connectionClasses = connectionManager.getConnectionClasses();
|
||||
connectionDescriptors = connectionManager.getConnectionDescriptors();
|
||||
}
|
||||
|
||||
List<ConcreteTest> resultingConcreteTests = new ArrayList<>(connectionClasses.size());
|
||||
List<ConcreteTest> resultingConcreteTests = new ArrayList<>(connectionDescriptors.size());
|
||||
|
||||
for (XmppConnectionDescriptor<?, ?, ?> connectionDescriptor : connectionDescriptors) {
|
||||
String connectionNick = connectionDescriptor.getNickname();
|
||||
|
||||
if (config.enabledConnections != null && !config.enabledConnections.contains(connectionNick)) {
|
||||
DisabledTest disabledTest = new DisabledTest(lowLevelTestMethod.testMethod, "Not creating test for " + lowLevelTestMethod + " with connection '" + connectionNick
|
||||
+ "', as this connection type is not enabled");
|
||||
testRunResult.disabledTests.add(disabledTest);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.disabledConnections != null && config.disabledConnections.contains(connectionNick)) {
|
||||
DisabledTest disabledTest = new DisabledTest(lowLevelTestMethod.testMethod, "Not creating test for " + lowLevelTestMethod + " with connection '" + connectionNick
|
||||
+ ", as this connection type is disabled");
|
||||
testRunResult.disabledTests.add(disabledTest);
|
||||
continue;
|
||||
}
|
||||
|
||||
Class<? extends AbstractXMPPConnection> connectionClass = connectionDescriptor.getConnectionClass();
|
||||
|
||||
for (Class<? extends AbstractXMPPConnection> connectionClass : connectionClasses) {
|
||||
ConcreteTest.Executor executor = () -> lowLevelTestMethod.invoke(test, connectionClass);
|
||||
ConcreteTest concreteTest = new ConcreteTest(TestType.LowLevel, lowLevelTestMethod.testMethod, executor, connectionClass.getSimpleName());
|
||||
resultingConcreteTests.add(concreteTest);
|
||||
|
@ -543,7 +529,7 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
return resultingConcreteTests;
|
||||
}
|
||||
|
||||
private <C extends AbstractXMPPConnection> void invokeSpecificLowLevel(LowLevelTestMethod testMethod,
|
||||
private static <C extends AbstractXMPPConnection> void invokeSpecificLowLevel(LowLevelTestMethod testMethod,
|
||||
AbstractSmackSpecificLowLevelIntegrationTest<C> test)
|
||||
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InterruptedException,
|
||||
SmackException, IOException, XMPPException {
|
||||
|
@ -554,7 +540,7 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
testMethod.invoke(test, connectionClass);
|
||||
}
|
||||
|
||||
protected SmackIntegrationTestEnvironment<DC> prepareEnvironment() throws SmackException,
|
||||
protected SmackIntegrationTestEnvironment prepareEnvironment() throws SmackException,
|
||||
IOException, XMPPException, InterruptedException, KeyManagementException,
|
||||
NoSuchAlgorithmException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||
return connectionManager.prepareEnvironment();
|
||||
|
@ -576,9 +562,6 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
|
||||
private static Exception throwFatalException(Throwable e) throws Error, NoResponseException,
|
||||
InterruptedException {
|
||||
if (e instanceof NoResponseException) {
|
||||
throw (NoResponseException) e;
|
||||
}
|
||||
if (e instanceof InterruptedException) {
|
||||
throw (InterruptedException) e;
|
||||
}
|
||||
|
@ -612,9 +595,13 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
private final List<SuccessfulTest> successfulIntegrationTests = Collections.synchronizedList(new LinkedList<SuccessfulTest>());
|
||||
private final List<FailedTest> failedIntegrationTests = Collections.synchronizedList(new LinkedList<FailedTest>());
|
||||
private final List<TestNotPossible> impossibleIntegrationTests = Collections.synchronizedList(new LinkedList<TestNotPossible>());
|
||||
|
||||
// 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.
|
||||
private final List<DisabledTestClass> disabledTestClasses = Collections.synchronizedList(new ArrayList<>());
|
||||
private final List<DisabledTest> disabledTests = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
private final Map<Class<? extends AbstractSmackIntTest>, Throwable> impossibleTestClasses = new HashMap<>();
|
||||
private final AtomicInteger numberOfAvailableTestMethods = new AtomicInteger();
|
||||
private final AtomicInteger numberOfPossibleTestMethods = new AtomicInteger();
|
||||
|
||||
TestRunResult() {
|
||||
}
|
||||
|
@ -624,11 +611,7 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
}
|
||||
|
||||
public int getNumberOfAvailableTests() {
|
||||
return numberOfAvailableTestMethods.get();
|
||||
}
|
||||
|
||||
public int getNumberOfPossibleTests() {
|
||||
return numberOfPossibleTestMethods.get();
|
||||
return successfulIntegrationTests.size() + failedIntegrationTests.size() + impossibleIntegrationTests.size();
|
||||
}
|
||||
|
||||
public List<SuccessfulTest> getSuccessfulTests() {
|
||||
|
@ -648,6 +631,77 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
}
|
||||
}
|
||||
|
||||
final class PreparedTest {
|
||||
private final AbstractSmackIntTest test;
|
||||
private final List<ConcreteTest> concreteTests;
|
||||
|
||||
private final Method beforeClassMethod;
|
||||
private final Method afterClassMethod;
|
||||
|
||||
private PreparedTest(AbstractSmackIntTest test, List<ConcreteTest> concreteTests) {
|
||||
this.test = test;
|
||||
this.concreteTests = concreteTests;
|
||||
Class<? extends AbstractSmackIntTest> testClass = test.getClass();
|
||||
|
||||
beforeClassMethod = getSinttestSpecialMethod(testClass, BeforeClass.class);
|
||||
afterClassMethod = getSinttestSpecialMethod(testClass, AfterClass.class);
|
||||
}
|
||||
|
||||
public void run() throws InterruptedException, XMPPException, IOException, SmackException {
|
||||
try {
|
||||
// Run the @BeforeClass methods (if any)
|
||||
executeSinttestSpecialMethod(beforeClassMethod);
|
||||
|
||||
for (ConcreteTest concreteTest : concreteTests) {
|
||||
runConcreteTest(concreteTest);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
executeSinttestSpecialMethod(afterClassMethod);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeSinttestSpecialMethod(Method method) {
|
||||
if (method == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
method.invoke(test);
|
||||
}
|
||||
catch (InvocationTargetException | IllegalAccessException e) {
|
||||
LOGGER.log(Level.SEVERE, "Exception executing " + method, e);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Method getSinttestSpecialMethod(Class<? extends AbstractSmackIntTest> testClass, Class<? extends Annotation> annotation) {
|
||||
Set<Method> specialClassMethods = getAllMethods(testClass,
|
||||
withAnnotation(annotation), withReturnType(Void.TYPE),
|
||||
withParametersCount(0), withModifier(Modifier.PUBLIC
|
||||
));
|
||||
|
||||
// See if there are any methods that have a special but a wrong signature
|
||||
Set<Method> allSpecialClassMethods = getAllMethods(testClass, withAnnotation(annotation));
|
||||
allSpecialClassMethods.removeAll(specialClassMethods);
|
||||
if (!allSpecialClassMethods.isEmpty()) {
|
||||
throw new IllegalArgumentException(annotation + " methods with wrong signature found");
|
||||
}
|
||||
|
||||
if (specialClassMethods.size() == 1) {
|
||||
return specialClassMethods.iterator().next();
|
||||
}
|
||||
else if (specialClassMethods.size() > 1) {
|
||||
throw new IllegalArgumentException("Only one @BeforeClass method allowed");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static final class ConcreteTest {
|
||||
private final TestType testType;
|
||||
private final Method method;
|
||||
|
@ -675,13 +729,8 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
.append(method.getName())
|
||||
.append(" (")
|
||||
.append(testType.name());
|
||||
final String SUBDESCRIPTION_DELIMITER = ", ";
|
||||
sb.append(SUBDESCRIPTION_DELIMITER);
|
||||
|
||||
for (String subdescripton : subdescriptons) {
|
||||
sb.append(subdescripton).append(SUBDESCRIPTION_DELIMITER);
|
||||
}
|
||||
sb.setLength(sb.length() - SUBDESCRIPTION_DELIMITER.length());
|
||||
sb.append(", ");
|
||||
StringUtils.appendTo(Arrays.asList(subdescriptons), sb);
|
||||
sb.append(')');
|
||||
|
||||
stringCache = sb.toString();
|
||||
|
@ -705,6 +754,50 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
}
|
||||
}
|
||||
|
||||
public static final class DisabledTestClass {
|
||||
private final Class<? extends AbstractSmackIntTest> testClass;
|
||||
private final String reason;
|
||||
|
||||
private DisabledTestClass(Class<? extends AbstractSmackIntTest> testClass, String reason) {
|
||||
this.testClass = testClass;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public Class<? extends AbstractSmackIntTest> getTestClass() {
|
||||
return testClass;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public StringBuilder appendTo(StringBuilder sb) {
|
||||
return sb.append("Disabled ").append(testClass).append(" because ").append(reason);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class DisabledTest {
|
||||
private final Method method;
|
||||
private final String reason;
|
||||
|
||||
private DisabledTest(Method method, String reason) {
|
||||
this.method = method;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public StringBuilder appendTo(StringBuilder sb) {
|
||||
return sb.append("Disabled ").append(method).append(" because ").append(reason);
|
||||
}
|
||||
}
|
||||
|
||||
private final class LowLevelTestMethod {
|
||||
private final Method testMethod;
|
||||
private final SmackIntegrationTest smackIntegrationTestAnnotation;
|
||||
|
@ -718,6 +811,7 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
parameterListOfConnections = testMethodParametersIsListOfConnections(testMethod);
|
||||
}
|
||||
|
||||
// TODO: The second parameter should probably be a connection descriptor?
|
||||
private void invoke(AbstractSmackLowLevelIntegrationTest test,
|
||||
Class<? extends AbstractXMPPConnection> connectionClass)
|
||||
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException,
|
||||
|
@ -746,6 +840,11 @@ public class SmackIntegrationTestFramework<DC extends AbstractXMPPConnection> {
|
|||
testMethod.invoke(test, connectionsArray);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return testMethod.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean testMethodParametersIsListOfConnections(Method testMethod) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2019 Florian Schmaus
|
||||
* Copyright 2018-2020 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,8 +29,13 @@ import java.util.List;
|
|||
import org.jivesoftware.smack.AbstractXMPPConnection;
|
||||
import org.jivesoftware.smack.ConnectionConfiguration;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.util.Consumer;
|
||||
|
||||
public class XmppConnectionDescriptor<C extends AbstractXMPPConnection, CC extends ConnectionConfiguration, CCB extends ConnectionConfiguration.Builder<?, CC>> {
|
||||
public final class XmppConnectionDescriptor<
|
||||
C extends AbstractXMPPConnection,
|
||||
CC extends ConnectionConfiguration,
|
||||
CCB extends ConnectionConfiguration.Builder<?, CC>
|
||||
> {
|
||||
|
||||
private final Class<C> connectionClass;
|
||||
private final Class<CC> connectionConfigurationClass;
|
||||
|
@ -38,13 +43,18 @@ public class XmppConnectionDescriptor<C extends AbstractXMPPConnection, CC exten
|
|||
private final Constructor<C> connectionConstructor;
|
||||
private final Method builderMethod;
|
||||
|
||||
public XmppConnectionDescriptor(Class<C> connectionClass, Class<CC> connectionConfigurationClass)
|
||||
throws ClassNotFoundException, NoSuchMethodException, SecurityException {
|
||||
this.connectionClass = connectionClass;
|
||||
this.connectionConfigurationClass = connectionConfigurationClass;
|
||||
private final Consumer<CCB> extraBuilder;
|
||||
|
||||
this.connectionConstructor = getConstructor(connectionClass, connectionConfigurationClass);
|
||||
this.builderMethod = getBuilderMethod(connectionConfigurationClass);
|
||||
private final String nickname;
|
||||
|
||||
private XmppConnectionDescriptor(Builder<C, CC, CCB> builder) throws NoSuchMethodException, SecurityException {
|
||||
connectionClass = builder.connectionClass;
|
||||
connectionConfigurationClass = builder.connectionConfigurationClass;
|
||||
extraBuilder = builder.extraBuilder;
|
||||
nickname = builder.nickname;
|
||||
|
||||
connectionConstructor = getConstructor(connectionClass, connectionConfigurationClass);
|
||||
builderMethod = getBuilderMethod(connectionConfigurationClass);
|
||||
}
|
||||
|
||||
public C construct(Configuration sinttestConfiguration)
|
||||
|
@ -65,6 +75,9 @@ public class XmppConnectionDescriptor<C extends AbstractXMPPConnection, CC exten
|
|||
Collection<ConnectionConfigurationBuilderApplier> customConnectionConfigurationAppliers)
|
||||
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||
CCB connectionConfigurationBuilder = getNewBuilder();
|
||||
if (extraBuilder != null) {
|
||||
extraBuilder.accept(connectionConfigurationBuilder);
|
||||
}
|
||||
for (ConnectionConfigurationBuilderApplier customConnectionConfigurationApplier : customConnectionConfigurationAppliers) {
|
||||
customConnectionConfigurationApplier.applyConfigurationTo(connectionConfigurationBuilder);
|
||||
}
|
||||
|
@ -88,6 +101,10 @@ public class XmppConnectionDescriptor<C extends AbstractXMPPConnection, CC exten
|
|||
return connectionClass;
|
||||
}
|
||||
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
private static <C extends XMPPConnection> Constructor<C> getConstructor(Class<C> connectionClass,
|
||||
Class<? extends ConnectionConfiguration> connectionConfigurationClass)
|
||||
throws NoSuchMethodException, SecurityException {
|
||||
|
@ -106,4 +123,47 @@ public class XmppConnectionDescriptor<C extends AbstractXMPPConnection, CC exten
|
|||
}
|
||||
return builderMethod;
|
||||
}
|
||||
|
||||
public static <C extends AbstractXMPPConnection, CC extends ConnectionConfiguration, CCB extends ConnectionConfiguration.Builder<?, CC>>
|
||||
Builder<C, CC, CCB> buildWith(Class<C> connectionClass, Class<CC> connectionConfigurationClass) {
|
||||
return buildWith(connectionClass, connectionConfigurationClass, null);
|
||||
}
|
||||
|
||||
public static <C extends AbstractXMPPConnection, CC extends ConnectionConfiguration, CCB extends ConnectionConfiguration.Builder<?, CC>>
|
||||
Builder<C, CC, CCB> buildWith(Class<C> connectionClass, Class<CC> connectionConfigurationClass, Class<CCB> connectionConfigurationBuilderClass) {
|
||||
return new Builder<>(connectionClass, connectionConfigurationClass, connectionConfigurationBuilderClass);
|
||||
}
|
||||
|
||||
public static final class Builder<C extends AbstractXMPPConnection, CC extends ConnectionConfiguration, CCB extends ConnectionConfiguration.Builder<?, CC>> {
|
||||
private final Class<C> connectionClass;
|
||||
private final Class<CC> connectionConfigurationClass;
|
||||
|
||||
private Consumer<CCB> extraBuilder;
|
||||
|
||||
private String nickname;
|
||||
|
||||
// The connectionConfigurationBuilderClass merely exists for type-checking purposes.
|
||||
@SuppressWarnings("UnusedVariable")
|
||||
private Builder(Class<C> connectionClass, Class<CC> connectionConfigurationClass,
|
||||
Class<CCB> connectionConfigurationBuilderClass) {
|
||||
this.connectionClass = connectionClass;
|
||||
this.connectionConfigurationClass = connectionConfigurationClass;
|
||||
|
||||
nickname = connectionClass.getSimpleName();
|
||||
}
|
||||
|
||||
public Builder<C, CC, CCB> applyExtraConfguration(Consumer<CCB> extraBuilder) {
|
||||
this.extraBuilder = extraBuilder;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<C, CC, CCB> withNickname(String nickname) {
|
||||
this.nickname = nickname;
|
||||
return this;
|
||||
}
|
||||
|
||||
public XmppConnectionDescriptor<C, CC, CCB> build() throws NoSuchMethodException, SecurityException {
|
||||
return new XmppConnectionDescriptor<>(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2019 Florian Schmaus
|
||||
* Copyright 2018-2020 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,7 +27,6 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
@ -38,9 +37,11 @@ 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.compression.CompressionModuleDescriptor;
|
||||
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
|
||||
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
|
||||
import org.jivesoftware.smack.tcp.XmppNioTcpConnection;
|
||||
import org.jivesoftware.smack.util.MultiMap;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
|
@ -54,55 +55,92 @@ import org.jxmpp.jid.impl.JidCreate;
|
|||
import org.jxmpp.jid.parts.Localpart;
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
|
||||
public class XmppConnectionManager<DC extends AbstractXMPPConnection> {
|
||||
public class XmppConnectionManager {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(XmppConnectionManager.class.getName());
|
||||
|
||||
private static final Map<Class<? extends AbstractXMPPConnection>, XmppConnectionDescriptor<? extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>>> CONNECTION_DESCRIPTORS = new ConcurrentHashMap<>();
|
||||
private static final XmppConnectionDescriptor<? extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>> DEFAULT_CONNECTION_DESCRIPTOR;
|
||||
|
||||
private static final Map<String, XmppConnectionDescriptor<? extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>>> NICKNAME_CONNECTION_DESCRIPTORS = new HashMap<>();
|
||||
|
||||
private static final MultiMap<
|
||||
Class<? extends AbstractXMPPConnection>,
|
||||
XmppConnectionDescriptor<? extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>>
|
||||
> CONNECTION_DESCRIPTORS = new MultiMap<>();
|
||||
|
||||
static {
|
||||
try {
|
||||
addConnectionDescriptor(XmppNioTcpConnection.class, XMPPTCPConnectionConfiguration.class);
|
||||
addConnectionDescriptor(XMPPTCPConnection.class, XMPPTCPConnectionConfiguration.class);
|
||||
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException e) {
|
||||
DEFAULT_CONNECTION_DESCRIPTOR = XmppConnectionDescriptor.buildWith(XMPPTCPConnection.class, XMPPTCPConnectionConfiguration.class)
|
||||
.withNickname("tcp")
|
||||
.build();
|
||||
addConnectionDescriptor(DEFAULT_CONNECTION_DESCRIPTOR);
|
||||
|
||||
addConnectionDescriptor(
|
||||
XmppConnectionDescriptor.buildWith(ModularXmppClientToServerConnection.class, ModularXmppClientToServerConnectionConfiguration.class)
|
||||
.withNickname("modular")
|
||||
.build()
|
||||
);
|
||||
addConnectionDescriptor(
|
||||
XmppConnectionDescriptor.buildWith(ModularXmppClientToServerConnection.class, ModularXmppClientToServerConnectionConfiguration.class, ModularXmppClientToServerConnectionConfiguration.Builder.class)
|
||||
.withNickname("modular-nocompress")
|
||||
.applyExtraConfguration(cb -> cb.removeModule(CompressionModuleDescriptor.class))
|
||||
.build()
|
||||
);
|
||||
} catch (NoSuchMethodException | SecurityException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addConnectionDescriptor(Class<? extends AbstractXMPPConnection> connectionClass,
|
||||
Class<? extends ConnectionConfiguration> connectionConfigurationClass) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
|
||||
XmppConnectionDescriptor<? extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>> connectionDescriptor = new XmppConnectionDescriptor<>(
|
||||
connectionClass, connectionConfigurationClass);
|
||||
addConnectionDescriptor(connectionDescriptor);
|
||||
}
|
||||
|
||||
public static void addConnectionDescriptor(
|
||||
public static boolean addConnectionDescriptor(
|
||||
XmppConnectionDescriptor<? extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>> connectionDescriptor) {
|
||||
String nickname = connectionDescriptor.getNickname();
|
||||
Class<? extends AbstractXMPPConnection> connectionClass = connectionDescriptor.getConnectionClass();
|
||||
|
||||
boolean alreadyExisted;
|
||||
synchronized (CONNECTION_DESCRIPTORS) {
|
||||
alreadyExisted = removeConnectionDescriptor(nickname);
|
||||
|
||||
CONNECTION_DESCRIPTORS.put(connectionClass, connectionDescriptor);
|
||||
NICKNAME_CONNECTION_DESCRIPTORS.put(connectionDescriptor.getNickname(), connectionDescriptor);
|
||||
}
|
||||
return alreadyExisted;
|
||||
}
|
||||
|
||||
public static void removeConnectionDescriptor(Class<? extends AbstractXMPPConnection> connectionClass) {
|
||||
CONNECTION_DESCRIPTORS.remove(connectionClass);
|
||||
public static boolean removeConnectionDescriptor(String nickname) {
|
||||
synchronized (CONNECTION_DESCRIPTORS) {
|
||||
XmppConnectionDescriptor<? extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>> connectionDescriptor = NICKNAME_CONNECTION_DESCRIPTORS.remove(nickname);
|
||||
if (connectionDescriptor == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private final XmppConnectionDescriptor<DC, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>> defaultConnectionDescriptor;
|
||||
boolean removed = CONNECTION_DESCRIPTORS.removeOne(connectionDescriptor.getConnectionClass(), connectionDescriptor);
|
||||
assert removed;
|
||||
}
|
||||
|
||||
private final Map<Class<? extends AbstractXMPPConnection>, XmppConnectionDescriptor<? extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>>> connectionDescriptors = new HashMap<>(
|
||||
CONNECTION_DESCRIPTORS.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
private final SmackIntegrationTestFramework<?> sinttestFramework;
|
||||
private final XmppConnectionDescriptor<? extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>> defaultConnectionDescriptor;
|
||||
|
||||
private final Map<String, XmppConnectionDescriptor<? extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>>> nicknameConnectionDescriptors;
|
||||
|
||||
private final MultiMap<
|
||||
Class<? extends AbstractXMPPConnection>,
|
||||
XmppConnectionDescriptor<? extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>>
|
||||
> connectionDescriptors;
|
||||
|
||||
private final SmackIntegrationTestFramework sinttestFramework;
|
||||
private final Configuration sinttestConfiguration;
|
||||
private final String testRunId;
|
||||
|
||||
private final DC accountRegistrationConnection;
|
||||
private final AbstractXMPPConnection accountRegistrationConnection;
|
||||
private final ServiceAdministrationManager adminManager;
|
||||
private final AccountManager accountManager;
|
||||
|
||||
/**
|
||||
* One of the three main connections. The type of the main connections is the default connection type.
|
||||
*/
|
||||
DC conOne, conTwo, conThree;
|
||||
AbstractXMPPConnection conOne, conTwo, conThree;
|
||||
|
||||
/**
|
||||
* A pool of authenticated and free to use connections.
|
||||
|
@ -114,20 +152,25 @@ public class XmppConnectionManager<DC extends AbstractXMPPConnection> {
|
|||
*/
|
||||
private final List<AbstractXMPPConnection> connections = new ArrayList<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
XmppConnectionManager(SmackIntegrationTestFramework<?> sinttestFramework,
|
||||
Class<? extends DC> defaultConnectionClass)
|
||||
XmppConnectionManager(SmackIntegrationTestFramework sinttestFramework)
|
||||
throws SmackException, IOException, XMPPException, InterruptedException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||
synchronized (CONNECTION_DESCRIPTORS) {
|
||||
connectionDescriptors = CONNECTION_DESCRIPTORS.clone();
|
||||
nicknameConnectionDescriptors = new HashMap<>(NICKNAME_CONNECTION_DESCRIPTORS);
|
||||
}
|
||||
|
||||
this.sinttestFramework = sinttestFramework;
|
||||
this.sinttestConfiguration = sinttestFramework.config;
|
||||
this.testRunId = sinttestFramework.testRunResult.testRunId;
|
||||
|
||||
connectionDescriptors.putAll(CONNECTION_DESCRIPTORS);
|
||||
|
||||
defaultConnectionDescriptor = (XmppConnectionDescriptor<DC, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>>) connectionDescriptors.get(
|
||||
defaultConnectionClass);
|
||||
String configuredDefaultConnectionNickname = sinttestConfiguration.defaultConnectionNickname;
|
||||
if (configuredDefaultConnectionNickname != null) {
|
||||
defaultConnectionDescriptor = nicknameConnectionDescriptors.get(configuredDefaultConnectionNickname);
|
||||
if (defaultConnectionDescriptor == null) {
|
||||
throw new IllegalArgumentException("Could not find a connection descriptor for " + defaultConnectionClass);
|
||||
throw new IllegalArgumentException("Could not find a connection descriptor for connection nickname '" + configuredDefaultConnectionNickname + "'");
|
||||
}
|
||||
} else {
|
||||
defaultConnectionDescriptor = DEFAULT_CONNECTION_DESCRIPTOR;
|
||||
}
|
||||
|
||||
switch (sinttestConfiguration.accountRegistration) {
|
||||
|
@ -157,11 +200,11 @@ public class XmppConnectionManager<DC extends AbstractXMPPConnection> {
|
|||
}
|
||||
}
|
||||
|
||||
SmackIntegrationTestEnvironment<DC> prepareEnvironment() throws KeyManagementException, NoSuchAlgorithmException,
|
||||
SmackIntegrationTestEnvironment prepareEnvironment() throws KeyManagementException, NoSuchAlgorithmException,
|
||||
InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
|
||||
SmackException, IOException, XMPPException, InterruptedException {
|
||||
prepareMainConnections();
|
||||
return new SmackIntegrationTestEnvironment<DC>(conOne, conTwo, conThree,
|
||||
return new SmackIntegrationTestEnvironment(conOne, conTwo, conThree,
|
||||
sinttestFramework.testRunResult.testRunId, sinttestConfiguration, this);
|
||||
}
|
||||
|
||||
|
@ -169,9 +212,9 @@ public class XmppConnectionManager<DC extends AbstractXMPPConnection> {
|
|||
IllegalAccessException, IllegalArgumentException, InvocationTargetException, SmackException, IOException,
|
||||
XMPPException, InterruptedException {
|
||||
final int mainAccountCount = AccountNum.values().length;
|
||||
List<DC> connections = new ArrayList<>(mainAccountCount);
|
||||
List<AbstractXMPPConnection> connections = new ArrayList<>(mainAccountCount);
|
||||
for (AccountNum mainAccountNum : AccountNum.values()) {
|
||||
DC mainConnection = getConnectedMainConnectionFor(mainAccountNum);
|
||||
AbstractXMPPConnection mainConnection = getConnectedMainConnectionFor(mainAccountNum);
|
||||
connections.add(mainConnection);
|
||||
}
|
||||
conOne = connections.get(0);
|
||||
|
@ -179,18 +222,18 @@ public class XmppConnectionManager<DC extends AbstractXMPPConnection> {
|
|||
conThree = connections.get(2);
|
||||
}
|
||||
|
||||
public Class<? extends AbstractXMPPConnection> getDefaultConnectionClass() {
|
||||
return defaultConnectionDescriptor.getConnectionClass();
|
||||
public XmppConnectionDescriptor<? extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>> getDefaultConnectionDescriptor() {
|
||||
return defaultConnectionDescriptor;
|
||||
}
|
||||
|
||||
public Set<Class<? extends AbstractXMPPConnection>> getConnectionClasses() {
|
||||
return Collections.unmodifiableSet(connectionDescriptors.keySet());
|
||||
public Collection<XmppConnectionDescriptor<? extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>>> getConnectionDescriptors() {
|
||||
return Collections.unmodifiableCollection(nicknameConnectionDescriptors.values());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends AbstractXMPPConnection> XmppConnectionDescriptor<C, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>> getConnectionDescriptorFor(
|
||||
Class<C> connectionClass) {
|
||||
return (XmppConnectionDescriptor<C, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>>) connectionDescriptors.get(
|
||||
return (XmppConnectionDescriptor<C, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>>) connectionDescriptors.getFirst(
|
||||
connectionClass);
|
||||
}
|
||||
|
||||
|
@ -249,7 +292,7 @@ public class XmppConnectionManager<DC extends AbstractXMPPConnection> {
|
|||
|
||||
private static final String USERNAME_PREFIX = "smack-inttest";
|
||||
|
||||
private DC getConnectedMainConnectionFor(AccountNum accountNum) throws SmackException, IOException, XMPPException,
|
||||
private AbstractXMPPConnection getConnectedMainConnectionFor(AccountNum accountNum) throws SmackException, IOException, XMPPException,
|
||||
InterruptedException, KeyManagementException, NoSuchAlgorithmException, InstantiationException,
|
||||
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||
String middlefix;
|
||||
|
@ -283,7 +326,7 @@ public class XmppConnectionManager<DC extends AbstractXMPPConnection> {
|
|||
registerAccount(finalAccountUsername, finalAccountPassword);
|
||||
}
|
||||
|
||||
DC mainConnection = defaultConnectionDescriptor.construct(sinttestConfiguration, builder -> {
|
||||
AbstractXMPPConnection mainConnection = defaultConnectionDescriptor.construct(sinttestConfiguration, builder -> {
|
||||
try {
|
||||
builder.setUsernameAndPassword(finalAccountUsername, finalAccountPassword)
|
||||
.setResource(middlefix + '-' + testRunId);
|
||||
|
@ -347,7 +390,7 @@ public class XmppConnectionManager<DC extends AbstractXMPPConnection> {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
XmppConnectionDescriptor<C, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>> connectionDescriptor = (XmppConnectionDescriptor<C, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder<?, ?>>) connectionDescriptors
|
||||
.get(connectionClass);
|
||||
.getFirst(connectionClass);
|
||||
for (int i = 0; i < count; i++) {
|
||||
C connection = constructConnectedConnection(connectionDescriptor);
|
||||
connections.add(connection);
|
||||
|
@ -367,7 +410,7 @@ public class XmppConnectionManager<DC extends AbstractXMPPConnection> {
|
|||
return connection;
|
||||
}
|
||||
|
||||
DC constructConnection()
|
||||
AbstractXMPPConnection constructConnection()
|
||||
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||
return constructConnection(defaultConnectionDescriptor);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2019 Florian Schmaus
|
||||
* Copyright 2015-2020 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -51,7 +51,7 @@ public class ChatTest extends AbstractSmackIntegrationTest {
|
|||
private boolean invoked;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public ChatTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public ChatTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
chatManagerOne = org.jivesoftware.smack.chat.ChatManager.getInstanceFor(conOne);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2019 Florian Schmaus
|
||||
* Copyright 2015-2020 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,7 @@ import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
|||
|
||||
public class LoginIntegrationTest extends AbstractSmackLowLevelIntegrationTest {
|
||||
|
||||
public LoginIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public LoginIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2019 Florian Schmaus
|
||||
* Copyright 2015-2020 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -35,7 +35,7 @@ import org.igniterealtime.smack.inttest.TestNotPossibleException;
|
|||
|
||||
public class StreamManagementTest extends AbstractSmackSpecificLowLevelIntegrationTest<XMPPTCPConnection> {
|
||||
|
||||
public StreamManagementTest(SmackIntegrationTestEnvironment<?> environment) throws Exception {
|
||||
public StreamManagementTest(SmackIntegrationTestEnvironment environment) throws Exception {
|
||||
super(environment, XMPPTCPConnection.class);
|
||||
XMPPTCPConnection connection = getSpecificUnconnectedConnection();
|
||||
connection.connect().login();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2019 Florian Schmaus
|
||||
* Copyright 2015-2020 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,7 +26,7 @@ import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
|||
|
||||
public class WaitForClosingStreamElementTest extends AbstractSmackLowLevelIntegrationTest {
|
||||
|
||||
public WaitForClosingStreamElementTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public WaitForClosingStreamElementTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2019 Florian Schmaus
|
||||
* Copyright 2018-2020 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,7 +19,7 @@ package org.jivesoftware.smack;
|
|||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.jivesoftware.smack.tcp.XmppNioTcpConnection;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection;
|
||||
|
||||
import org.igniterealtime.smack.XmppConnectionStressTest;
|
||||
import org.igniterealtime.smack.XmppConnectionStressTest.StressTestFailedException.ErrorsWhileSendingOrReceivingException;
|
||||
|
@ -30,7 +30,7 @@ import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
|||
|
||||
public class XmppConnectionIntegrationTest extends AbstractSmackLowLevelIntegrationTest {
|
||||
|
||||
public XmppConnectionIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public XmppConnectionIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
}
|
||||
|
||||
|
@ -52,12 +52,12 @@ public class XmppConnectionIntegrationTest extends AbstractSmackLowLevelIntegrat
|
|||
|
||||
final Level connectionStatsLogLevel = Level.FINE;
|
||||
if (LOGGER.isLoggable(connectionStatsLogLevel)) {
|
||||
if (connections.get(0) instanceof XmppNioTcpConnection) {
|
||||
if (connections.get(0) instanceof ModularXmppClientToServerConnection) {
|
||||
for (XMPPConnection connection : connections) {
|
||||
XmppNioTcpConnection xmppNioTcpConnection = (XmppNioTcpConnection) connection;
|
||||
XmppNioTcpConnection.Stats stats = xmppNioTcpConnection.getStats();
|
||||
ModularXmppClientToServerConnection xmppC2sConnection = (ModularXmppClientToServerConnection) connection;
|
||||
ModularXmppClientToServerConnection.Stats stats = xmppC2sConnection.getStats();
|
||||
LOGGER.log(connectionStatsLogLevel,
|
||||
"Connections stats for " + xmppNioTcpConnection + ":\n{}",
|
||||
"Connections stats for " + xmppC2sConnection + ":\n{}",
|
||||
stats);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Florian Schmaus
|
||||
* Copyright 2018-2020 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,7 @@ public abstract class AbstractChatIntegrationTest extends AbstractSmackIntegrati
|
|||
protected final ChatManager chatManagerTwo;
|
||||
protected final ChatManager chatManagerThree;
|
||||
|
||||
protected AbstractChatIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
protected AbstractChatIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
chatManagerOne = ChatManager.getInstanceFor(conOne);
|
||||
chatManagerTwo = ChatManager.getInstanceFor(conTwo);
|
||||
|
|
|
@ -26,7 +26,7 @@ import org.jxmpp.jid.EntityBareJid;
|
|||
|
||||
public class IncomingMessageListenerIntegrationTest extends AbstractChatIntegrationTest {
|
||||
|
||||
public IncomingMessageListenerIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public IncomingMessageListenerIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ import org.jxmpp.jid.EntityBareJid;
|
|||
|
||||
public class OutgoingMessageListenerIntegrationTest extends AbstractChatIntegrationTest {
|
||||
|
||||
public OutgoingMessageListenerIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public OutgoingMessageListenerIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2016-2019 Florian Schmaus
|
||||
* Copyright 2016-2020 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,7 +30,7 @@ import org.jxmpp.jid.FullJid;
|
|||
|
||||
public class LowLevelRosterIntegrationTest extends AbstractSmackLowLevelIntegrationTest {
|
||||
|
||||
public LowLevelRosterIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public LowLevelRosterIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2019 Florian Schmaus
|
||||
* Copyright 2015-2020 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -38,7 +38,7 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest {
|
|||
private final Roster rosterOne;
|
||||
private final Roster rosterTwo;
|
||||
|
||||
public RosterIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public RosterIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
rosterOne = Roster.getInstanceFor(conOne);
|
||||
rosterTwo = Roster.getInstanceFor(conTwo);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2019 Florian Schmaus
|
||||
* Copyright 2018-2020 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,21 +22,22 @@ import java.security.NoSuchAlgorithmException;
|
|||
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection;
|
||||
|
||||
import org.igniterealtime.smack.inttest.AbstractSmackSpecificLowLevelIntegrationTest;
|
||||
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
||||
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||
|
||||
public class XmppNioTcpConnectionLowLevelIntegrationTest extends AbstractSmackSpecificLowLevelIntegrationTest<XmppNioTcpConnection> {
|
||||
public class XmppNioTcpConnectionLowLevelIntegrationTest extends AbstractSmackSpecificLowLevelIntegrationTest<ModularXmppClientToServerConnection> {
|
||||
|
||||
public XmppNioTcpConnectionLowLevelIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
super(environment, XmppNioTcpConnection.class);
|
||||
public XmppNioTcpConnectionLowLevelIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment, ModularXmppClientToServerConnection.class);
|
||||
}
|
||||
|
||||
@SmackIntegrationTest
|
||||
public void testDisconnectAfterConnect() throws KeyManagementException, NoSuchAlgorithmException, SmackException,
|
||||
IOException, XMPPException, InterruptedException {
|
||||
XmppNioTcpConnection connection = getSpecificUnconnectedConnection();
|
||||
ModularXmppClientToServerConnection connection = getSpecificUnconnectedConnection();
|
||||
|
||||
connection.connect();
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2013-2019 Florian Schmaus
|
||||
* Copyright 2013-2020 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -57,7 +57,7 @@ public class EntityCapsTest extends AbstractSmackIntegrationTest {
|
|||
private final ServiceDiscoveryManager sdmOne;
|
||||
private final ServiceDiscoveryManager sdmTwo;
|
||||
|
||||
public EntityCapsTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public EntityCapsTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
ecmTwo = EntityCapsManager.getInstanceFor(environment.conTwo);
|
||||
sdmOne = ServiceDiscoveryManager.getInstanceFor(environment.conOne);
|
||||
|
|
|
@ -55,7 +55,7 @@ public class ChatStateIntegrationTest extends AbstractSmackIntegrationTest {
|
|||
};
|
||||
|
||||
|
||||
public ChatStateIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public ChatStateIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2019 Florian Schmaus
|
||||
* Copyright 2015-2020 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,7 +39,7 @@ public class FileTransferIntegrationTest extends AbstractSmackIntegrationTest {
|
|||
private final FileTransferManager ftManagerOne;
|
||||
private final FileTransferManager ftManagerTwo;
|
||||
|
||||
public FileTransferIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public FileTransferIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
ftManagerOne = FileTransferManager.getInstanceFor(conOne);
|
||||
ftManagerTwo = FileTransferManager.getInstanceFor(conTwo);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017-2019 Florian Schmaus
|
||||
* Copyright 2017-2020 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,7 +43,7 @@ public class HttpFileUploadIntegrationTest extends AbstractSmackIntegrationTest
|
|||
|
||||
private final HttpFileUploadManager hfumOne;
|
||||
|
||||
public HttpFileUploadIntegrationTest(SmackIntegrationTestEnvironment<?> environment) throws XMPPErrorException,
|
||||
public HttpFileUploadIntegrationTest(SmackIntegrationTestEnvironment environment) throws XMPPErrorException,
|
||||
NotConnectedException, NoResponseException, InterruptedException, TestNotPossibleException {
|
||||
super(environment);
|
||||
hfumOne = HttpFileUploadManager.getInstanceFor(conOne);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2016-2019 Florian Schmaus
|
||||
* Copyright 2016-2020 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,7 +43,7 @@ public class IoTControlIntegrationTest extends AbstractSmackIntegrationTest {
|
|||
|
||||
private final IoTControlManager IoTControlManagerTwo;
|
||||
|
||||
public IoTControlIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public IoTControlIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
IoTControlManagerOne = IoTControlManager.getInstanceFor(conOne);
|
||||
IoTControlManagerTwo = IoTControlManager.getInstanceFor(conTwo);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2016-2019 Florian Schmaus
|
||||
* Copyright 2016-2020 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -45,7 +45,7 @@ public class IoTDataIntegrationTest extends AbstractSmackIntegrationTest {
|
|||
|
||||
private final IoTDataManager iotDataManagerTwo;
|
||||
|
||||
public IoTDataIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public IoTDataIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
iotDataManagerOne = IoTDataManager.getInstanceFor(conOne);
|
||||
iotDataManagerTwo = IoTDataManager.getInstanceFor(conTwo);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2016-2019 Florian Schmaus
|
||||
* Copyright 2016-2020 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,7 +41,7 @@ public class IoTDiscoveryIntegrationTest extends AbstractSmackIntegrationTest {
|
|||
private final IoTDiscoveryManager discoveryManagerOne;
|
||||
private final IoTDiscoveryManager discoveryManagerTwo;
|
||||
|
||||
public IoTDiscoveryIntegrationTest(SmackIntegrationTestEnvironment<?> environment) throws NoResponseException,
|
||||
public IoTDiscoveryIntegrationTest(SmackIntegrationTestEnvironment environment) throws NoResponseException,
|
||||
XMPPErrorException, NotConnectedException, InterruptedException, TestNotPossibleException {
|
||||
super(environment);
|
||||
discoveryManagerOne = IoTDiscoveryManager.getInstanceFor(conOne);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2019 Florian Schmaus
|
||||
* Copyright 2015-2020 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -31,7 +31,7 @@ import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
|||
|
||||
public class VersionIntegrationTest extends AbstractSmackIntegrationTest {
|
||||
|
||||
public VersionIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public VersionIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2016 Fernando Ramirez, 2018-2019 Florian Schmaus
|
||||
* Copyright 2016 Fernando Ramirez, 2018-2020 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -48,7 +48,7 @@ public class MamIntegrationTest extends AbstractSmackIntegrationTest {
|
|||
|
||||
private final MamManager mamManagerConTwo;
|
||||
|
||||
public MamIntegrationTest(SmackIntegrationTestEnvironment<?> environment) throws NoResponseException,
|
||||
public MamIntegrationTest(SmackIntegrationTestEnvironment environment) throws NoResponseException,
|
||||
XMPPErrorException, NotConnectedException, InterruptedException, TestNotPossibleException, NotLoggedInException {
|
||||
super(environment);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Paul Schaub, 2019 Florian Schmaus.
|
||||
* Copyright 2018 Paul Schaub, 2019-2020 Florian Schmaus.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -31,7 +31,7 @@ public class MoodIntegrationTest extends AbstractSmackIntegrationTest {
|
|||
private final MoodManager mm1;
|
||||
private final MoodManager mm2;
|
||||
|
||||
public MoodIntegrationTest(SmackIntegrationTestEnvironment<?> environment) {
|
||||
public MoodIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
mm1 = MoodManager.getInstanceFor(conOne);
|
||||
mm2 = MoodManager.getInstanceFor(conTwo);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2019 Florian Schmaus
|
||||
* Copyright 2015-2020 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,7 +54,7 @@ public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest {
|
|||
private final MultiUserChatManager mucManagerTwo;
|
||||
private final DomainBareJid mucService;
|
||||
|
||||
public MultiUserChatIntegrationTest(SmackIntegrationTestEnvironment<?> environment)
|
||||
public MultiUserChatIntegrationTest(SmackIntegrationTestEnvironment environment)
|
||||
throws NoResponseException, XMPPErrorException, NotConnectedException,
|
||||
InterruptedException, TestNotPossibleException {
|
||||
super(environment);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015-2019 Florian Schmaus
|
||||
* Copyright 2015-2020 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,7 +40,7 @@ import org.jxmpp.jid.parts.Resourcepart;
|
|||
|
||||
public class MultiUserChatLowLevelIntegrationTest extends AbstractSmackLowLevelIntegrationTest {
|
||||
|
||||
public MultiUserChatLowLevelIntegrationTest(SmackIntegrationTestEnvironment<?> environment) throws Exception {
|
||||
public MultiUserChatLowLevelIntegrationTest(SmackIntegrationTestEnvironment environment) throws Exception {
|
||||
super(environment);
|
||||
AbstractXMPPConnection connection = getConnectedConnection();
|
||||
try {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue