diff --git a/build.gradle b/build.gradle
index 2e9b23ecb..117cf8a7d 100644
--- a/build.gradle
+++ b/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 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) {
diff --git a/documentation/connection-modules.md b/documentation/connection-modules.md
new file mode 100644
index 000000000..50110e6f7
--- /dev/null
+++ b/documentation/connection-modules.md
@@ -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.
diff --git a/documentation/developer/integrationtest.md b/documentation/developer/integrationtest.md
index 2bf2143db..e9caed4e3 100644
--- a/documentation/developer/integrationtest.md
+++ b/documentation/developer/integrationtest.md
@@ -58,24 +58,28 @@ debugger=console
### Framework properties
-| Name | |
-|----------------------|-------------------------------------------|
-| 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' |
-| replyTimeout | In milliseconds |
-| adminAccountUsername | Username of the XEP-0133 Admin account |
-| adminAccountPassword | Password of the XEP-0133 Admin account |
-| accountOneUsername | Username of the first XMPP account |
-| accountOnePassword | Password of the first XMPP account |
-| accountTwoUsername | Username of the second XMPP account |
-| accountTwoPassword | Password of the second XMPP account |
-| accountThreeUsername | Username of the third XMPP account |
-| accountThreePassword | Password of the third XMPP account |
-| debugger | 'console' for console debugger, 'enhanced' for the enhanced debugger |
-| enabledTests | List of enabled tests |
-| disabledTests | List of disabled tests |
-| testPackages | List of packages with tests |
+| 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' |
+| replyTimeout | In milliseconds |
+| adminAccountUsername | Username of the XEP-0133 Admin account |
+| adminAccountPassword | Password of the XEP-0133 Admin account |
+| accountOneUsername | Username of the first XMPP account |
+| accountOnePassword | Password of the first XMPP account |
+| accountTwoUsername | Username of the second XMPP account |
+| accountTwoPassword | Password of the second XMPP account |
+| accountThreeUsername | Username of the third XMPP account |
+| accountThreePassword | Password of the third XMPP account |
+| 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.
diff --git a/documentation/index.md b/documentation/index.md
index 034f18070..5b9d27e9f 100644
--- a/documentation/index.md
+++ b/documentation/index.md
@@ -13,3 +13,4 @@
* [Debugging with Smack](debugging.md)
* [Smack Extensions Manual](extensions/index.md)
+ * [Smack's Modular Connection Architecture](connection-modules.md)
diff --git a/settings.gradle b/settings.gradle
index d3b53d80e..0f84a8753 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -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',
diff --git a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java
index 824dedc44..c3d7109f8 100644
--- a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java
+++ b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java
@@ -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
diff --git a/smack-core/build.gradle b/smack-core/build.gradle
index 8d29cfd89..d9da388b1 100644
--- a/smack-core/build.gradle
+++ b/smack-core/build.gradle
@@ -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 {
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java
index 0e13c28c7..3c20fce8b 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2009 Jive Software, 2018-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 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 populateHostAddresses() {
- List 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);
}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXmppNioConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXmppNioConnection.java
deleted file mode 100644
index 8655db39f..000000000
--- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXmppNioConnection.java
+++ /dev/null
@@ -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 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);
- }
-
-}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java
index 33d1bfdd4..3d59269c7 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2003-2007 Jive Software, 2017-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.
*
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java
index cd49af68e..9e15ef8b0 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java
@@ -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;
@@ -379,4 +381,19 @@ public final class SmackConfiguration {
return defaultConcurrencyLevelLimit;
}
+ private static final Set> 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);
+ }
+ }
+ }
}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java
index 41b3b417f..fbf6ebd40 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java
@@ -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,6 +94,7 @@ 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());
}
@@ -264,45 +269,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;
+
+ /**
+ * Deprecated, do not use.
+ *
+ * @param wrappedThrowable the wrapped throwable.
+ */
+ @Deprecated
+ public GenericConnectionException(Throwable wrappedThrowable) {
+ super(wrappedThrowable);
+ }
+ }
+
/**
- * 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()}.
+ * 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 class ConnectionException extends SmackException {
+ public static final class EndpointConnectionException extends ConnectionException {
/**
*
*/
- private static final long serialVersionUID = 1686944201672697996L;
+ private static final long serialVersionUID = 1;
- private final List failedAddresses;
+ private final List lookupFailures;
+ private final List extends RemoteConnectionException>> connectionExceptions;
- public ConnectionException(Throwable wrappedThrowable) {
- super(wrappedThrowable);
- failedAddresses = new ArrayList<>(0);
- }
-
- private ConnectionException(String message, List failedAddresses) {
+ private EndpointConnectionException(String message, List 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 failedAddresses) {
- final String DELIMITER = ", ";
- StringBuilder sb = new StringBuilder("The following addresses failed: ");
- for (HostAddress hostAddress : failedAddresses) {
- sb.append(hostAddress.getErrorMessage());
- sb.append(DELIMITER);
+ public static EndpointConnectionException from(List 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);
}
- // Remove the last delimiter
- sb.setLength(sb.length() - DELIMITER.length());
- return new ConnectionException(sb.toString(), 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 getFailedAddresses() {
- return failedAddresses;
+ public List 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 lookupFailures;
+
+ private NoEndpointsDiscoveredException(String message, List lookupFailures) {
+ super(message);
+ this.lookupFailures = Collections.unmodifiableList(lookupFailures);
+ }
+
+ public List getLookupFailures() {
+ return lookupFailures;
+ }
+
+ public static NoEndpointsDiscoveredException from(List 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);
}
}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java
index b08a50603..08646728a 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java
@@ -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 implements Future,
private ExceptionCallback exceptionCallback;
+ private Consumer> completionCallback;
+
@Override
public final synchronized boolean cancel(boolean mayInterruptIfRunning) {
if (isDone()) {
@@ -87,6 +92,11 @@ public abstract class SmackFuture implements Future,
return this;
}
+ public void onCompletion(Consumer> 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 implements Future,
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 implements Future,
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);
+ }
}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java
index 92c122571..2d5aeb30e 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java
@@ -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;
}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java
index 1c2477789..76d028204 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2018-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();
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/XmppInputOutputFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/XmppInputOutputFilter.java
index b7b14ce05..18432f564 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/XmppInputOutputFilter.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/XmppInputOutputFilter.java
@@ -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();
}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/bind2/Bind2Module.java b/smack-core/src/main/java/org/jivesoftware/smack/bind2/Bind2Module.java
new file mode 100644
index 000000000..53162ddce
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/bind2/Bind2Module.java
@@ -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 {
+
+ 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);
+ }
+
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/bind2/Bind2ModuleDescriptor.java b/smack-core/src/main/java/org/jivesoftware/smack/bind2/Bind2ModuleDescriptor.java
new file mode 100644
index 000000000..bf09a4522
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/bind2/Bind2ModuleDescriptor.java
@@ -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> 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;
+ }
+ }
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/bind2/package-info.java b/smack-core/src/main/java/org/jivesoftware/smack/bind2/package-info.java
new file mode 100644
index 000000000..bc422ed02
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/bind2/package-info.java
@@ -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;
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java
new file mode 100644
index 000000000..9694b47b8
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java
@@ -0,0 +1,1114 @@
+/**
+ *
+ * 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;
+
+import java.io.IOException;
+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.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.net.ssl.SSLSession;
+
+import org.jivesoftware.smack.AbstractXMPPConnection;
+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.SmackFuture;
+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.c2s.XmppClientToServerTransport.LookupConnectionEndpointsFailed;
+import org.jivesoftware.smack.c2s.XmppClientToServerTransport.LookupConnectionEndpointsResult;
+import org.jivesoftware.smack.c2s.XmppClientToServerTransport.LookupConnectionEndpointsSuccess;
+import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
+import org.jivesoftware.smack.c2s.internal.WalkStateGraphContext;
+import org.jivesoftware.smack.fsm.ConnectionStateEvent;
+import org.jivesoftware.smack.fsm.ConnectionStateMachineListener;
+import org.jivesoftware.smack.fsm.LoginContext;
+import org.jivesoftware.smack.fsm.NoOpState;
+import org.jivesoftware.smack.fsm.State;
+import org.jivesoftware.smack.fsm.StateDescriptor;
+import org.jivesoftware.smack.fsm.StateDescriptorGraph;
+import org.jivesoftware.smack.fsm.StateDescriptorGraph.GraphVertex;
+import org.jivesoftware.smack.fsm.StateMachineException;
+import org.jivesoftware.smack.fsm.StateTransitionResult;
+import org.jivesoftware.smack.fsm.StateTransitionResult.AttemptResult;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smack.packet.Nonza;
+import org.jivesoftware.smack.packet.Presence;
+import org.jivesoftware.smack.packet.Stanza;
+import org.jivesoftware.smack.packet.StreamClose;
+import org.jivesoftware.smack.packet.StreamError;
+import org.jivesoftware.smack.packet.TopLevelStreamElement;
+import org.jivesoftware.smack.packet.XmlEnvironment;
+import org.jivesoftware.smack.parsing.SmackParsingException;
+import org.jivesoftware.smack.sasl.SASLErrorException;
+import org.jivesoftware.smack.sasl.SASLMechanism;
+import org.jivesoftware.smack.util.ArrayBlockingQueueWithShutdown;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smack.xml.XmlPullParser;
+import org.jivesoftware.smack.xml.XmlPullParserException;
+
+import org.jxmpp.jid.parts.Resourcepart;
+
+public final class ModularXmppClientToServerConnection extends AbstractXMPPConnection {
+
+ private static final Logger LOGGER = Logger.getLogger(ModularXmppClientToServerConnectionConfiguration.class.getName());
+
+ private final ArrayBlockingQueueWithShutdown outgoingElementsQueue = new ArrayBlockingQueueWithShutdown<>(
+ 100, true);
+
+ private XmppClientToServerTransport activeTransport;
+
+ private final List connectionStateMachineListeners = new CopyOnWriteArrayList<>();
+
+ private boolean featuresReceived;
+
+ protected boolean streamResumed;
+
+ private GraphVertex currentStateVertex;
+
+ private List walkFromDisconnectToAuthenticated;
+
+ private final ModularXmppClientToServerConnectionConfiguration configuration;
+
+ private final ModularXmppClientToServerConnectionInternal connectionInternal;
+
+ private final Map, ModularXmppClientToServerConnectionModule extends ModularXmppClientToServerConnectionModuleDescriptor>> connectionModules = new HashMap<>();
+
+ private final Map, XmppClientToServerTransport> transports = new HashMap<>();
+ /**
+ * This is one of those cases where the field is modified by one thread and read by another. We currently use
+ * CopyOnWriteArrayList but should potentially use a VarHandle once Smack supports them.
+ */
+ private final List inputOutputFilters = new CopyOnWriteArrayList<>();
+
+ private List previousInputOutputFilters;
+
+ public ModularXmppClientToServerConnection(ModularXmppClientToServerConnectionConfiguration configuration) {
+ super(configuration);
+
+ this.configuration = configuration;
+
+ // Construct the internal connection API.
+ connectionInternal = new ModularXmppClientToServerConnectionInternal(this, getReactor(), debugger, outgoingElementsQueue) {
+
+ @Override
+ public void parseAndProcessElement(String wrappedCompleteElement) {
+ ModularXmppClientToServerConnection.this.parseAndProcessElement(wrappedCompleteElement);
+ }
+
+ @Override
+ public void notifyConnectionError(Exception e) {
+ ModularXmppClientToServerConnection.this.notifyConnectionError(e);
+ }
+
+ @Override
+ public void onStreamOpen(XmlPullParser parser) {
+ ModularXmppClientToServerConnection.this.onStreamOpen(parser);
+ }
+
+ @Override
+ public void onStreamClosed() {
+ ModularXmppClientToServerConnection.this.closingStreamReceived.reportSuccess();
+ }
+
+ @Override
+ public void fireFirstLevelElementSendListeners(TopLevelStreamElement element) {
+ ModularXmppClientToServerConnection.this.firePacketSendingListeners(element);
+ }
+
+ @Override
+ public void invokeConnectionStateMachineListener(ConnectionStateEvent connectionStateEvent) {
+ ModularXmppClientToServerConnection.this.invokeConnectionStateMachineListener(connectionStateEvent);
+ }
+
+ @Override
+ public XmlEnvironment getOutgoingStreamXmlEnvironment() {
+ return outgoingStreamXmlEnvironment;
+ }
+
+ @Override
+ public void addXmppInputOutputFilter(XmppInputOutputFilter xmppInputOutputFilter) {
+ inputOutputFilters.add(0, xmppInputOutputFilter);
+ }
+
+ @Override
+ public ListIterator getXmppInputOutputFilterBeginIterator() {
+ return inputOutputFilters.listIterator();
+ }
+
+ @Override
+ public ListIterator getXmppInputOutputFilterEndIterator() {
+ return inputOutputFilters.listIterator(inputOutputFilters.size());
+ }
+
+ @Override
+ public void newStreamOpenWaitForFeaturesSequence(String waitFor) throws InterruptedException,
+ ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException {
+ ModularXmppClientToServerConnection.this.newStreamOpenWaitForFeaturesSequence(waitFor);
+ }
+
+ @Override
+ public SmackTlsContext getSmackTlsContext()
+ throws KeyManagementException, NoSuchAlgorithmException, CertificateException, IOException,
+ UnrecoverableKeyException, KeyStoreException, NoSuchProviderException {
+ return ModularXmppClientToServerConnection.this.getSmackTlsContext();
+ }
+
+ @Override
+ public SN sendAndWaitForResponse(Nonza nonza, Class successNonzaClass,
+ Class failedNonzaClass) throws NoResponseException, NotConnectedException, FailedNonzaException, InterruptedException {
+ return ModularXmppClientToServerConnection.this.sendAndWaitForResponse(nonza, successNonzaClass, failedNonzaClass);
+ }
+
+ @Override
+ public void asyncGo(Runnable runnable) {
+ AbstractXMPPConnection.asyncGo(runnable);
+ }
+
+ @Override
+ public Exception getCurrentConnectionException() {
+ return ModularXmppClientToServerConnection.this.currentConnectionException;
+ }
+
+ @Override
+ public void setCompressionEnabled(boolean compressionEnabled) {
+ ModularXmppClientToServerConnection.this.compressionEnabled = compressionEnabled;
+ }
+
+ @Override
+ public void setTransport(XmppClientToServerTransport xmppTransport) {
+ ModularXmppClientToServerConnection.this.activeTransport = xmppTransport;
+ }
+
+ };
+
+ // Construct the modules from the module descriptor. We do this before constructing the state graph, as the
+ // modules are sometimes used to construct the states.
+ for (ModularXmppClientToServerConnectionModuleDescriptor moduleDescriptor : configuration.moduleDescriptors) {
+ Class extends ModularXmppClientToServerConnectionModuleDescriptor> moduleDescriptorClass = moduleDescriptor.getClass();
+ ModularXmppClientToServerConnectionModule extends ModularXmppClientToServerConnectionModuleDescriptor> connectionModule = moduleDescriptor.constructXmppConnectionModule(connectionInternal);
+ connectionModules.put(moduleDescriptorClass, connectionModule);
+
+ XmppClientToServerTransport transport = connectionModule.getTransport();
+ // Not every module may provide a transport.
+ if (transport != null) {
+ transports.put(moduleDescriptorClass, transport);
+ }
+ }
+
+ GraphVertex initialStateDescriptorVertex = configuration.initialStateDescriptorVertex;
+ // Convert the graph of state descriptors to a graph of states, bound to this very connection.
+ currentStateVertex = StateDescriptorGraph.convertToStateGraph(initialStateDescriptorVertex, connectionInternal);
+ }
+
+ @SuppressWarnings("unchecked")
+ public > CM getConnectionModuleFor(
+ Class extends ModularXmppClientToServerConnectionModuleDescriptor> descriptorClass) {
+ return (CM) connectionModules.get(descriptorClass);
+ }
+
+ @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 WalkStateGraphContext.Builder buildNewWalkTo(Class extends StateDescriptor> finalStateClass) {
+ return WalkStateGraphContext.builder(currentStateVertex.getElement().getStateDescriptor().getClass(), finalStateClass);
+ }
+
+ /**
+ * Unwind the state. This will revert the effects of the state by calling {@link State#resetState()} prior issuing a
+ * connection state event of {@link ConnectionStateEvent#StateRevertBackwardsWalk}.
+ *
+ * @param revertedState the state which is going to get reverted.
+ */
+ private void unwindState(State revertedState) {
+ invokeConnectionStateMachineListener(new ConnectionStateEvent.StateRevertBackwardsWalk(revertedState));
+ revertedState.resetState();
+ }
+
+ protected void walkStateGraph(WalkStateGraphContext walkStateGraphContext) throws XMPPErrorException,
+ SASLErrorException, FailedNonzaException, IOException, SmackException, InterruptedException {
+ // Save a copy of the current state
+ GraphVertex previousStateVertex = currentStateVertex;
+ try {
+ walkStateGraphInternal(walkStateGraphContext);
+ } catch (XMPPErrorException | SASLErrorException | FailedNonzaException | IOException | SmackException
+ | InterruptedException e) {
+ currentStateVertex = previousStateVertex;
+ // Unwind the state.
+ State revertedState = currentStateVertex.getElement();
+ unwindState(revertedState);
+ throw e;
+ }
+ }
+
+ private void walkStateGraphInternal(WalkStateGraphContext walkStateGraphContext) throws XMPPErrorException,
+ SASLErrorException, IOException, SmackException, InterruptedException, FailedNonzaException {
+ // Save a copy of the current state
+ final GraphVertex initialStateVertex = currentStateVertex;
+ final State initialState = initialStateVertex.getElement();
+ final StateDescriptor initialStateDescriptor = initialState.getStateDescriptor();
+
+ walkStateGraphContext.recordWalkTo(initialState);
+
+ // Check if this is the walk's final state.
+ if (walkStateGraphContext.isWalksFinalState(initialStateDescriptor)) {
+ // 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> outgoingStateEdges = initialStateVertex.getOutgoingEdges();
+
+ // See if we need to handle mandatory intermediate states.
+ GraphVertex mandatoryIntermediateStateVertex = walkStateGraphContext.maybeReturnMandatoryImmediateState(outgoingStateEdges);
+ if (mandatoryIntermediateStateVertex != null) {
+ StateTransitionResult reason = attemptEnterState(mandatoryIntermediateStateVertex, walkStateGraphContext);
+
+ if (reason instanceof StateTransitionResult.Success) {
+ walkStateGraph(walkStateGraphContext);
+ return;
+ }
+
+ // We could not enter a mandatory intermediate state. Throw here.
+ throw new StateMachineException.SmackMandatoryStateFailedException(
+ mandatoryIntermediateStateVertex.getElement(), reason);
+ }
+
+ for (Iterator> it = outgoingStateEdges.iterator(); it.hasNext();) {
+ GraphVertex successorStateVertex = it.next();
+ State successorState = successorStateVertex.getElement();
+
+ // Ignore successorStateVertex if the only way to the final state is via the initial state. This happens
+ // typically if we are in the ConnectedButUnauthenticated state on the way to ResourceboundAndAuthenticated,
+ // where we do not want to walk via InstantShutdown/Shtudown in a cycle over the initial state towards this
+ // state.
+ if (walkStateGraphContext.wouldCauseCycle(successorStateVertex)) {
+ // Ignore this successor.
+ invokeConnectionStateMachineListener(new ConnectionStateEvent.TransitionIgnoredDueCycle(initialStateVertex, successorStateVertex));
+ } else {
+ StateTransitionResult result = attemptEnterState(successorStateVertex, walkStateGraphContext);
+
+ if (result instanceof StateTransitionResult.Success) {
+ 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 (result != null) {
+ walkStateGraphContext.recordFailedState(successorState, result);
+ }
+ }
+
+ if (!it.hasNext()) {
+ throw StateMachineException.SmackStateGraphDeadEndException.from(walkStateGraphContext,
+ initialStateVertex);
+ }
+ }
+
+ // Walk the state graph by recursion.
+ walkStateGraph(walkStateGraphContext);
+ }
+
+ /**
+ * Attempt to enter a state. Note that this method may return null if this state can be safely ignored ignored.
+ *
+ * @param successorStateVertex the successor state vertex.
+ * @param walkStateGraphContext the "walk state graph" context.
+ * @return A state transition result or null if this state can be ignored.
+ * @throws SmackException if Smack detected an exceptional situation.
+ * @throws XMPPErrorException if an XMPP protocol error was received.
+ * @throws SASLErrorException if a SASL protocol error was returned.
+ * @throws IOException if an I/O error occurred.
+ * @throws InterruptedException if the calling thread was interrupted.
+ * @throws FailedNonzaException if an XMPP protocol failure was received.
+ */
+ private StateTransitionResult attemptEnterState(GraphVertex successorStateVertex,
+ WalkStateGraphContext walkStateGraphContext) throws SmackException, XMPPErrorException,
+ SASLErrorException, IOException, InterruptedException, FailedNonzaException {
+ final GraphVertex initialStateVertex = currentStateVertex;
+ final State initialState = initialStateVertex.getElement();
+ final State successorState = successorStateVertex.getElement();
+ final StateDescriptor successorStateDescriptor = successorState.getStateDescriptor();
+
+ if (!successorStateDescriptor.isMultiVisitState()
+ && walkStateGraphContext.stateAlreadyVisited(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()) {
+ StateTransitionResult.TransitionImpossibleBecauseNotImplemented transtionImpossibleBecauseNotImplemented = new StateTransitionResult.TransitionImpossibleBecauseNotImplemented(
+ successorStateDescriptor);
+ invokeConnectionStateMachineListener(new ConnectionStateEvent.TransitionNotPossible(initialState, successorState,
+ transtionImpossibleBecauseNotImplemented));
+ return transtionImpossibleBecauseNotImplemented;
+ }
+
+ final StateTransitionResult.AttemptResult transitionAttemptResult;
+ try {
+ StateTransitionResult.TransitionImpossible transitionImpossible = successorState.isTransitionToPossible(
+ walkStateGraphContext);
+ if (transitionImpossible != null) {
+ invokeConnectionStateMachineListener(new ConnectionStateEvent.TransitionNotPossible(initialState, successorState,
+ transitionImpossible));
+ return transitionImpossible;
+ }
+
+ invokeConnectionStateMachineListener(new ConnectionStateEvent.AboutToTransitionInto(initialState, successorState));
+ transitionAttemptResult = successorState.transitionInto(walkStateGraphContext);
+ } catch (SmackException | XMPPErrorException | SASLErrorException | IOException | InterruptedException
+ | FailedNonzaException e) {
+ // Unwind the state here too, since this state will not be unwound by walkStateGraph(), as it will not
+ // become a predecessor state in the walk.
+ unwindState(successorState);
+ throw e;
+ }
+ if (transitionAttemptResult instanceof StateTransitionResult.Failure) {
+ StateTransitionResult.Failure transitionFailureResult = (StateTransitionResult.Failure) transitionAttemptResult;
+ invokeConnectionStateMachineListener(
+ new ConnectionStateEvent.TransitionFailed(initialState, successorState, transitionFailureResult));
+ return transitionAttemptResult;
+ }
+
+ // If transitionAttemptResult is not an instance of TransitionFailureResult, then it has to be of type
+ // TransitionSuccessResult.
+ StateTransitionResult.Success transitionSuccessResult = (StateTransitionResult.Success) transitionAttemptResult;
+
+ currentStateVertex = successorStateVertex;
+ invokeConnectionStateMachineListener(
+ new ConnectionStateEvent.SuccessfullyTransitionedInto(successorState, transitionSuccessResult));
+
+ return transitionSuccessResult;
+ }
+
+ @Override
+ protected void sendStanzaInternal(Stanza stanza) throws NotConnectedException, InterruptedException {
+ sendTopLevelStreamElement(stanza);
+ }
+
+ @Override
+ public void sendNonza(Nonza nonza) throws NotConnectedException, InterruptedException {
+ sendTopLevelStreamElement(nonza);
+ }
+
+ private void sendTopLevelStreamElement(TopLevelStreamElement element) throws NotConnectedException, InterruptedException {
+ final XmppClientToServerTransport transport = activeTransport;
+ if (transport == null) {
+ throw new NotConnectedException();
+ }
+
+ outgoingElementsQueue.put(element);
+ transport.notifyAboutNewOutgoingElements();
+ }
+
+ @Override
+ protected void shutdown() {
+ shutdown(false);
+ }
+
+ @Override
+ public synchronized void instantShutdown() {
+ shutdown(true);
+ }
+
+ @Override
+ public ModularXmppClientToServerConnectionConfiguration getConfiguration() {
+ return configuration;
+ }
+
+ private void shutdown(boolean instant) {
+ Class extends StateDescriptor> mandatoryIntermediateState;
+ if (instant) {
+ mandatoryIntermediateState = InstantShutdownStateDescriptor.class;
+ } else {
+ mandatoryIntermediateState = ShutdownStateDescriptor.class;
+ }
+
+ WalkStateGraphContext context = buildNewWalkTo(
+ DisconnectedStateDescriptor.class).withMandatoryIntermediateState(
+ mandatoryIntermediateState).build();
+
+ try {
+ walkStateGraph(context);
+ } catch (XMPPErrorException | SASLErrorException | IOException | SmackException | InterruptedException
+ | FailedNonzaException e) {
+ throw new IllegalStateException("A walk to disconnected state should never throw", e);
+ }
+ }
+
+ protected SSLSession getSSLSession() {
+ final XmppClientToServerTransport transport = activeTransport;
+ if (transport == null) {
+ return null;
+ }
+ return transport.getSslSession();
+ }
+
+ @Override
+ protected void afterFeaturesReceived() {
+ featuresReceived = true;
+ synchronized (this) {
+ notifyAll();
+ }
+ }
+
+ protected void parseAndProcessElement(String element) {
+ try {
+ 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();
+ }
+ } catch (XmlPullParserException | IOException | InterruptedException | StreamErrorException
+ | SmackParsingException e) {
+ notifyConnectionError(e);
+ }
+ }
+
+ 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);
+ }
+
+ public static class DisconnectedStateDescriptor extends StateDescriptor {
+ protected DisconnectedStateDescriptor() {
+ super(DisconnectedState.class, StateDescriptor.Property.finalState);
+ addSuccessor(LookupRemoteConnectionEndpointsStateDescriptor.class);
+ }
+ }
+
+ private final class DisconnectedState extends State {
+
+ private DisconnectedState(StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) {
+ super(stateDescriptor, connectionInternal);
+ }
+
+ @Override
+ public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) {
+ synchronized (ModularXmppClientToServerConnection.this) {
+ if (inputOutputFilters.isEmpty()) {
+ previousInputOutputFilters = null;
+ } else {
+ previousInputOutputFilters = new ArrayList<>(inputOutputFilters.size());
+ previousInputOutputFilters.addAll(inputOutputFilters);
+ inputOutputFilters.clear();
+ }
+ }
+
+ // Reset all states we have visited when transitioning from disconnected to authenticated. This assumes that
+ // every state after authenticated does not need to be reset.
+ ListIterator it = walkFromDisconnectToAuthenticated.listIterator(
+ walkFromDisconnectToAuthenticated.size());
+ while (it.hasPrevious()) {
+ State stateToReset = it.previous();
+ stateToReset.resetState();
+ }
+ walkFromDisconnectToAuthenticated = null;
+
+ return StateTransitionResult.Success.EMPTY_INSTANCE;
+ }
+ }
+
+ public static final class LookupRemoteConnectionEndpointsStateDescriptor extends StateDescriptor {
+ private LookupRemoteConnectionEndpointsStateDescriptor() {
+ super(LookupRemoteConnectionEndpointsState.class);
+ }
+ }
+
+ private final class LookupRemoteConnectionEndpointsState extends State {
+ boolean outgoingElementsQueueWasShutdown;
+
+ private LookupRemoteConnectionEndpointsState(StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) {
+ super(stateDescriptor, connectionInternal);
+ }
+
+ @Override
+ public AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) throws XMPPErrorException,
+ SASLErrorException, IOException, SmackException, InterruptedException, FailedNonzaException {
+ // There is a challenge here: We are going to trigger the discovery of endpoints which will run
+ // asynchronously. After a timeout, all discovered endpoints are collected. To prevent stale results from
+ // previous discover runs, the results are communicated via SmackFuture, so that we always handle the most
+ // up-to-date results.
+
+ Map>> lookupFutures = new HashMap<>(
+ transports.size());
+
+ final int numberOfFutures;
+ {
+ List> allFutures = new ArrayList<>();
+ for (XmppClientToServerTransport transport : transports.values()) {
+ // First we clear the transport of any potentially previously discovered connection endpoints.
+ transport.resetDiscoveredConnectionEndpoints();
+
+ // Ask the transport to start the discovery of remote connection endpoints asynchronously.
+ List> transportFutures = transport.lookupConnectionEndpoints();
+
+ lookupFutures.put(transport, transportFutures);
+ allFutures.addAll(transportFutures);
+ }
+
+ numberOfFutures = allFutures.size();
+
+ // Wait until all features are ready or if the timeout occurs. Note that we do not inspect and react the
+ // return value of SmackFuture.await() as we want to collect the LookupConnectionEndpointsFailed later.
+ SmackFuture.await(allFutures, getReplyTimeout(), TimeUnit.MILLISECONDS);
+ }
+
+ // Note that we do not pass the lookupFailures in case there is at least one successful transport. The
+ // lookup failures are also recording in LookupConnectionEndpointsSuccess, e.g. as part of
+ // RemoteXmppTcpConnectionEndpoints.Result.
+ List lookupFailures = new ArrayList<>(numberOfFutures);
+
+ boolean atLeastOneConnectionEndpointDiscovered = false;
+ for (Map.Entry>> entry : lookupFutures.entrySet()) {
+ XmppClientToServerTransport transport = entry.getKey();
+
+ for (SmackFuture future : entry.getValue()) {
+ LookupConnectionEndpointsResult result = future.getIfAvailable();
+
+ if (result == null) {
+ continue;
+ }
+
+ if (result instanceof LookupConnectionEndpointsFailed) {
+ LookupConnectionEndpointsFailed lookupFailure = (LookupConnectionEndpointsFailed) result;
+ lookupFailures.add(lookupFailure);
+ continue;
+ }
+
+ LookupConnectionEndpointsSuccess successResult = (LookupConnectionEndpointsSuccess) result;
+
+ // Arm the transport with the success result, so that its information can be used by the transport
+ // to establish the connection.
+ transport.loadConnectionEndpoints(successResult);
+
+ // Mark that the connection attempt can continue.
+ atLeastOneConnectionEndpointDiscovered = true;
+ }
+ }
+
+ if (!atLeastOneConnectionEndpointDiscovered) {
+ throw SmackException.NoEndpointsDiscoveredException.from(lookupFailures);
+ }
+
+ // Even though the outgoing elements queue is unrelated to the lookup remote connection endpoints state, we
+ // do start the queue at this point. The transports will need it available, and we use the state's reset()
+ // function to close the queue again on failure.
+ outgoingElementsQueueWasShutdown = outgoingElementsQueue.start();
+
+ return StateTransitionResult.Success.EMPTY_INSTANCE;
+ }
+
+ @Override
+ public void resetState() {
+ for (XmppClientToServerTransport transport : transports.values()) {
+ transport.resetDiscoveredConnectionEndpoints();
+ }
+
+ if (outgoingElementsQueueWasShutdown) {
+ // Reset the outgoing elements queue in this state, since we also start it in this state.
+ outgoingElementsQueue.shutdown();
+ }
+ }
+ }
+
+ public static final class ConnectedButUnauthenticatedStateDescriptor extends StateDescriptor {
+ private ConnectedButUnauthenticatedStateDescriptor() {
+ super(ConnectedButUnauthenticatedState.class, StateDescriptor.Property.finalState);
+ addSuccessor(SaslAuthenticationStateDescriptor.class);
+ addSuccessor(InstantShutdownStateDescriptor.class);
+ addSuccessor(ShutdownStateDescriptor.class);
+ }
+ }
+
+ private final class ConnectedButUnauthenticatedState extends State {
+ private ConnectedButUnauthenticatedState(StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) {
+ super(stateDescriptor, connectionInternal);
+ }
+
+ @Override
+ public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) {
+ assert walkFromDisconnectToAuthenticated == null;
+
+ if (walkStateGraphContext.isWalksFinalState(getStateDescriptor())) {
+ // If this is the final state, then record the walk so far.
+ walkFromDisconnectToAuthenticated = walkStateGraphContext.getWalk();
+ }
+
+ connected = true;
+ return StateTransitionResult.Success.EMPTY_INSTANCE;
+ }
+
+ @Override
+ public void resetState() {
+ connected = false;
+ }
+ }
+
+ public 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, ModularXmppClientToServerConnectionInternal connectionInternal) {
+ super(stateDescriptor, connectionInternal);
+ }
+
+ @Override
+ public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext)
+ throws XMPPErrorException, SASLErrorException, IOException, SmackException,
+ InterruptedException {
+ prepareToWaitForFeaturesReceived();
+
+ LoginContext loginContext = walkStateGraphContext.getLoginContext();
+ 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 StateTransitionResult.Success {
+ private final String saslMechanismName;
+
+ private SaslAuthenticationSuccessResult(SASLMechanism usedSaslMechanism) {
+ super("SASL authentication successfull using " + usedSaslMechanism.getName());
+ this.saslMechanismName = usedSaslMechanism.getName();
+ }
+
+ public String getSaslMechanismName() {
+ return saslMechanismName;
+ }
+ }
+
+ public static final class AuthenticatedButUnboundStateDescriptor extends StateDescriptor {
+ private AuthenticatedButUnboundStateDescriptor() {
+ super(StateDescriptor.Property.multiVisitState);
+ addSuccessor(ResourceBindingStateDescriptor.class);
+ }
+ }
+
+ public 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, ModularXmppClientToServerConnectionInternal connectionInternal) {
+ super(stateDescriptor, connectionInternal);
+ }
+
+ @Override
+ public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext)
+ throws XMPPErrorException, SASLErrorException, IOException, SmackException,
+ InterruptedException {
+ // Calling bindResourceAndEstablishSession() below requires the lastFeaturesReceived sync point to be signaled.
+ // Since we entered this state, the FSM has decided that the last features have been received, hence signal
+ // the sync point.
+ lastFeaturesReceived.reportSuccess();
+
+ LoginContext loginContext = walkStateGraphContext.getLoginContext();
+ Resourcepart resource = bindResourceAndEstablishSession(loginContext.resource);
+
+ // TODO: This should be a field in the Stream Management (SM) module. Here should be hook which the SM
+ // module can use to set the field instead.
+ streamResumed = false;
+
+ return new ResourceBoundResult(resource, loginContext.resource);
+ }
+ }
+
+ public static final class ResourceBoundResult extends StateTransitionResult.Success {
+ private final Resourcepart resource;
+
+ private ResourceBoundResult(Resourcepart boundResource, Resourcepart requestedResource) {
+ super("Resource '" + boundResource + "' bound (requested: '" + requestedResource + "')");
+ this.resource = boundResource;
+ }
+
+ public Resourcepart getResource() {
+ return resource;
+ }
+ }
+
+ private boolean compressionEnabled;
+
+ @Override
+ public boolean isUsingCompression() {
+ return compressionEnabled;
+ }
+
+ public static final class AuthenticatedAndResourceBoundStateDescriptor extends StateDescriptor {
+ private AuthenticatedAndResourceBoundStateDescriptor() {
+ super(AuthenticatedAndResourceBoundState.class, StateDescriptor.Property.finalState);
+ addSuccessor(InstantShutdownStateDescriptor.class);
+ addSuccessor(ShutdownStateDescriptor.class);
+ }
+ }
+
+ private final class AuthenticatedAndResourceBoundState extends State {
+ private AuthenticatedAndResourceBoundState(StateDescriptor stateDescriptor,
+ ModularXmppClientToServerConnectionInternal connectionInternal) {
+ super(stateDescriptor, connectionInternal);
+ }
+
+ @Override
+ public StateTransitionResult.AttemptResult 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.getWalk().get(0).getStateDescriptor().getClass()
+ != DisconnectedStateDescriptor.class;
+ // Append the current walk to the previous one.
+ walkStateGraphContext.appendWalkTo(walkFromDisconnectToAuthenticated);
+ } else {
+ walkFromDisconnectToAuthenticated = new ArrayList<>(
+ walkStateGraphContext.getWalkLength() + 1);
+ walkStateGraphContext.appendWalkTo(walkFromDisconnectToAuthenticated);
+ }
+ walkFromDisconnectToAuthenticated.add(this);
+
+ afterSuccessfulLogin(streamResumed);
+
+ return StateTransitionResult.Success.EMPTY_INSTANCE;
+ }
+
+ @Override
+ public void resetState() {
+ authenticated = false;
+ }
+ }
+
+ static final class ShutdownStateDescriptor extends StateDescriptor {
+ private ShutdownStateDescriptor() {
+ super(ShutdownState.class);
+ addSuccessor(CloseConnectionStateDescriptor.class);
+ }
+ }
+
+ private final class ShutdownState extends State {
+ private ShutdownState(StateDescriptor stateDescriptor,
+ ModularXmppClientToServerConnectionInternal connectionInternal) {
+ super(stateDescriptor, connectionInternal);
+ }
+
+ @Override
+ public StateTransitionResult.TransitionImpossible isTransitionToPossible(WalkStateGraphContext walkStateGraphContext) {
+ ensureNotOnOurWayToAuthenticatedAndResourceBound(walkStateGraphContext);
+ return null;
+ }
+
+ @Override
+ public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) {
+ closingStreamReceived.init();
+
+ boolean streamCloseIssued = outgoingElementsQueue.offerAndShutdown(StreamClose.INSTANCE);
+
+ if (streamCloseIssued) {
+ activeTransport.notifyAboutNewOutgoingElements();
+
+ boolean successfullyReceivedStreamClose = waitForClosingStreamTagFromServer();
+
+ if (successfullyReceivedStreamClose) {
+ for (Iterator it = connectionInternal.getXmppInputOutputFilterBeginIterator(); it.hasNext();) {
+ XmppInputOutputFilter filter = it.next();
+ filter.closeInputOutput();
+ }
+
+ // Closing the filters may produced new outgoing data. Notify the transport about it.
+ activeTransport.afterFiltersClosed();
+
+ for (Iterator it = connectionInternal.getXmppInputOutputFilterBeginIterator(); it.hasNext();) {
+ XmppInputOutputFilter filter = it.next();
+ try {
+ filter.waitUntilInputOutputClosed();
+ } catch (IOException | CertificateException | InterruptedException | SmackException e) {
+ LOGGER.log(Level.WARNING, "waitUntilInputOutputClosed() threw", e);
+ }
+ }
+
+ // For correctness we set authenticated to false here, even though we will later again set it to
+ // false in the disconnected state.
+ authenticated = false;
+ }
+ }
+
+ return StateTransitionResult.Success.EMPTY_INSTANCE;
+ }
+ }
+
+ static final class InstantShutdownStateDescriptor extends StateDescriptor {
+ private InstantShutdownStateDescriptor() {
+ super(InstantShutdownState.class);
+ addSuccessor(CloseConnectionStateDescriptor.class);
+ }
+ }
+
+ private static final class InstantShutdownState extends NoOpState {
+ private InstantShutdownState(ModularXmppClientToServerConnection connection, StateDescriptor stateDescriptor, ModularXmppClientToServerConnectionInternal connectionInternal) {
+ super(connection, stateDescriptor, connectionInternal);
+ }
+
+ @Override
+ public StateTransitionResult.TransitionImpossible isTransitionToPossible(WalkStateGraphContext walkStateGraphContext) {
+ ensureNotOnOurWayToAuthenticatedAndResourceBound(walkStateGraphContext);
+ return null;
+ }
+ }
+
+ private static final class CloseConnectionStateDescriptor extends StateDescriptor {
+ private CloseConnectionStateDescriptor() {
+ super(CloseConnectionState.class);
+ addSuccessor(DisconnectedStateDescriptor.class);
+ }
+ }
+
+ private final class CloseConnectionState extends State {
+ private CloseConnectionState(StateDescriptor stateDescriptor,
+ ModularXmppClientToServerConnectionInternal connectionInternal) {
+ super(stateDescriptor, connectionInternal);
+ }
+
+ @Override
+ public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) {
+ activeTransport.disconnect();
+ activeTransport = null;
+
+ authenticated = connected = false;
+
+ return StateTransitionResult.Success.EMPTY_INSTANCE;
+ }
+ }
+
+ 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);
+ }
+ });
+ }
+
+ @Override
+ public boolean isSecureConnection() {
+ final XmppClientToServerTransport transport = activeTransport;
+ if (transport == null) {
+ return false;
+ }
+ return transport.isTransportSecured();
+ }
+
+ @Override
+ protected void connectInternal() throws SmackException, IOException, XMPPException, InterruptedException {
+ WalkStateGraphContext walkStateGraphContext = buildNewWalkTo(ConnectedButUnauthenticatedStateDescriptor.class)
+ .build();
+ walkStateGraph(walkStateGraphContext);
+ }
+
+ protected Map getFilterStats() {
+ Collection filters;
+ synchronized (this) {
+ if (inputOutputFilters.isEmpty() && previousInputOutputFilters != null) {
+ filters = previousInputOutputFilters;
+ } else {
+ filters = inputOutputFilters;
+ }
+ }
+
+ Map filterStats = new HashMap<>(filters.size());
+ for (XmppInputOutputFilter xmppInputOutputFilter : filters) {
+ Object stats = xmppInputOutputFilter.getStats();
+ String filterName = xmppInputOutputFilter.getFilterName();
+
+ filterStats.put(filterName, stats);
+ }
+
+ return filterStats;
+ }
+
+ public Stats getStats() {
+ Map, XmppClientToServerTransport.Stats> transportsStats = new HashMap<>(
+ transports.size());
+ for (Map.Entry, XmppClientToServerTransport> entry : transports.entrySet()) {
+ XmppClientToServerTransport.Stats transportStats = entry.getValue().getStats();
+
+ transportsStats.put(entry.getKey(), transportStats);
+ }
+
+ Map filterStats = getFilterStats();
+
+ return new Stats(transportsStats, filterStats);
+ }
+
+ public static final class Stats {
+ public final Map, XmppClientToServerTransport.Stats> transportsStats;
+ public final Map filtersStats;
+
+ private Stats(Map, XmppClientToServerTransport.Stats> transportsStats,
+ Map filtersStats) {
+ this.transportsStats = Collections.unmodifiableMap(transportsStats);
+ this.filtersStats = Collections.unmodifiableMap(filtersStats);
+ }
+
+ public void appendStatsTo(Appendable appendable) throws IOException {
+ StringUtils.appendHeading(appendable, "Connection stats", '#').append('\n');
+
+ for (Map.Entry, XmppClientToServerTransport.Stats> entry : transportsStats.entrySet()) {
+ Class extends ModularXmppClientToServerConnectionModuleDescriptor> transportClass = entry.getKey();
+ XmppClientToServerTransport.Stats stats = entry.getValue();
+
+ StringUtils.appendHeading(appendable, transportClass.getName());
+ appendable.append(stats.toString()).append('\n');
+ }
+
+ for (Map.Entry entry : filtersStats.entrySet()) {
+ String filterName = entry.getKey();
+ Object filterStats = entry.getValue();
+
+ StringUtils.appendHeading(appendable, filterName);
+ appendable.append(filterStats.toString()).append('\n');
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ try {
+ appendStatsTo(sb);
+ } catch (IOException e) {
+ // Should never happen.
+ throw new AssertionError(e);
+ }
+ return sb.toString();
+ }
+ }
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnectionConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnectionConfiguration.java
new file mode 100644
index 000000000..a3bf16911
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnectionConfiguration.java
@@ -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 moduleDescriptors;
+
+ final GraphVertex initialStateDescriptorVertex;
+
+ private ModularXmppClientToServerConnectionConfiguration(Builder builder) {
+ super(builder);
+
+ moduleDescriptors = Collections.unmodifiableSet(CollectionUtil.newSetWith(builder.modulesDescriptors.values()));
+
+ Set> 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> 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 {
+
+ private final Map, 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 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;
+ }
+ }
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnectionModule.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnectionModule.java
new file mode 100644
index 000000000..eef942ebb
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnectionModule.java
@@ -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 {
+
+ 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;
+ }
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnectionModuleDescriptor.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnectionModuleDescriptor.java
new file mode 100644
index 000000000..2c1054a36
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnectionModuleDescriptor.java
@@ -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> 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;
+ }
+
+ }
+
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/XmppClientToServerTransport.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/XmppClientToServerTransport.java
new file mode 100644
index 000000000..d68440fc3
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/XmppClientToServerTransport.java
@@ -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> 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()?
+ }
+
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/ModularXmppClientToServerConnectionInternal.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/ModularXmppClientToServerConnectionInternal.java
new file mode 100644
index 000000000..57452f270
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/ModularXmppClientToServerConnectionInternal.java
@@ -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 outgoingElementsQueue;
+
+ public ModularXmppClientToServerConnectionInternal(ModularXmppClientToServerConnection connection, SmackReactor reactor,
+ SmackDebugger smackDebugger, Queue 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 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 getXmppInputOutputFilterBeginIterator();
+
+ public abstract ListIterator 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 sendAndWaitForResponse(Nonza nonza,
+ Class successNonzaClass, Class 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);
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/WalkStateGraphContext.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/WalkStateGraphContext.java
new file mode 100644
index 000000000..ed2895d97
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/WalkStateGraphContext.java
@@ -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 walkedStateGraphPath = new ArrayList<>();
+
+ /**
+ * A linked Map of failed States with their reason as value.
+ */
+ final Map 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 maybeReturnMandatoryImmediateState(List> outgoingStateEdges) {
+ for (GraphVertex outgoingStateVertex : outgoingStateEdges) {
+ if (outgoingStateVertex.getElement().getStateDescriptor().getClass() == mandatoryIntermediateState) {
+ mandatoryIntermediateStateHandled = true;
+ return outgoingStateVertex;
+ }
+ }
+
+ return null;
+ }
+
+ public List getWalk() {
+ return CollectionUtil.newListWith(walkedStateGraphPath);
+ }
+
+ public int getWalkLength() {
+ return walkedStateGraphPath.size();
+ }
+
+ public void appendWalkTo(List 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 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 true if it would loop, false otherwise.
+ */
+ public boolean wouldCauseCycle(GraphVertex successorStateVertex) {
+ Set> visited = new HashSet<>();
+ return wouldCycleRecursive(successorStateVertex, visited);
+ }
+
+ private boolean wouldCycleRecursive(GraphVertex stateVertex, Set> 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 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);
+ }
+ }
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/package-info.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/package-info.java
new file mode 100644
index 000000000..9705f1cb1
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/package-info.java
@@ -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;
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/package-info.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/package-info.java
new file mode 100644
index 000000000..2c97e4c92
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/package-info.java
@@ -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;
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/compression/CompressionModule.java b/smack-core/src/main/java/org/jivesoftware/smack/compression/CompressionModule.java
new file mode 100644
index 000000000..2a018899b
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/compression/CompressionModule.java
@@ -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 {
+
+ 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);
+ }
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/compression/CompressionModuleDescriptor.java b/smack-core/src/main/java/org/jivesoftware/smack/compression/CompressionModuleDescriptor.java
new file mode 100644
index 000000000..3cf43ee79
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/compression/CompressionModuleDescriptor.java
@@ -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> 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;
+ }
+
+ }
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/compression/zlib/ZlibXmppCompressionFactory.java b/smack-core/src/main/java/org/jivesoftware/smack/compression/zlib/ZlibXmppCompressionFactory.java
index cbb295c1b..096147931 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/compression/zlib/ZlibXmppCompressionFactory.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/compression/zlib/ZlibXmppCompressionFactory.java
@@ -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 {
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/fsm/AbstractXmppStateMachineConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/fsm/AbstractXmppStateMachineConnection.java
deleted file mode 100644
index 2c7168148..000000000
--- a/smack-core/src/main/java/org/jivesoftware/smack/fsm/AbstractXmppStateMachineConnection.java
+++ /dev/null
@@ -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 connectionStateMachineListeners = new CopyOnWriteArrayList<>();
-
- private boolean featuresReceived;
-
- protected boolean streamResumed;
-
- private GraphVertex currentStateVertex;
-
- private List walkFromDisconnectToAuthenticated;
-
- private final List inputOutputFilters = new CopyOnWriteArrayList<>();
- private List previousInputOutputFilters;
-
- protected AbstractXmppStateMachineConnection(ConnectionConfiguration configuration, GraphVertex 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 walkedStateGraphPath = new ArrayList<>();
-
- /**
- * A linked Map of failed States with their reason as value.
- */
- private final Map 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 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 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> 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 mandatoryIntermediateStateVertex = null;
- for (GraphVertex 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> it = outgoingStateEdges.iterator(); it.hasNext();) {
- GraphVertex 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 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 getXmppInputOutputFilterBeginIterator() {
- return inputOutputFilters.listIterator();
- }
-
- protected final ListIterator getXmppInputOutputFilterEndIterator() {
- return inputOutputFilters.listIterator(inputOutputFilters.size());
- }
-
- protected final synchronized List
*
* @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 null
*/
- protected List lookupHostAddress0(DnsName name, List failedAddresses, DnssecMode dnssecMode) {
+ protected List lookupHostAddress0(DnsName name, List 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 hostAddresses) {
+ protected static boolean shouldContinue(CharSequence name, CharSequence hostname, List hostAddresses) {
if (hostAddresses == null) {
return true;
}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/dns/HostAddress.java b/smack-core/src/main/java/org/jivesoftware/smack/util/dns/HostAddress.java
deleted file mode 100644
index 83300bbc6..000000000
--- a/smack-core/src/main/java/org/jivesoftware/smack/util/dns/HostAddress.java
+++ /dev/null
@@ -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 exceptions = new LinkedHashMap<>();
- private final List 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 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 null in case there host address is only numeric, i.e. an IP address.
- *
- * @return the fully qualified domain name or null
- */
- 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 getExceptions() {
- return Collections.unmodifiableMap(exceptions);
- }
-
- public List 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> iterator = exceptions.entrySet().iterator();
- while (iterator.hasNext()) {
- Entry 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();
- }
-}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/dns/SRVRecord.java b/smack-core/src/main/java/org/jivesoftware/smack/util/dns/SRVRecord.java
deleted file mode 100644
index e55dc08f1..000000000
--- a/smack-core/src/main/java/org/jivesoftware/smack/util/dns/SRVRecord.java
+++ /dev/null
@@ -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 RFC 2782: A DNS RR for specifying the location of services (DNS
- * SRV)
- * @author Florian Schmaus
- *
- */
-public class SRVRecord extends HostAddress implements Comparable {
-
- 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 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;
- }
-}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/rce/RemoteConnectionEndpoint.java b/smack-core/src/main/java/org/jivesoftware/smack/util/rce/RemoteConnectionEndpoint.java
new file mode 100644
index 000000000..0fc31e85f
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/util/rce/RemoteConnectionEndpoint.java
@@ -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 {
+ 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 + ')';
+ }
+ }
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/rce/RemoteConnectionEndpointLookupFailure.java b/smack-core/src/main/java/org/jivesoftware/smack/util/rce/RemoteConnectionEndpointLookupFailure.java
new file mode 100644
index 000000000..73ce8a4b0
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/util/rce/RemoteConnectionEndpointLookupFailure.java
@@ -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;
+ }
+ }
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/rce/RemoteConnectionException.java b/smack-core/src/main/java/org/jivesoftware/smack/util/rce/RemoteConnectionException.java
new file mode 100644
index 000000000..581b58a28
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/util/rce/RemoteConnectionException.java
@@ -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 {
+
+ private final RemoteConnectionEndpoint.InetSocketAddressCoupling address;
+ private final Exception exception;
+
+ public RemoteConnectionException(RCE remoteConnectionEndpoint, InetAddress inetAddress,
+ Exception exception) {
+ this(new RemoteConnectionEndpoint.InetSocketAddressCoupling<>(remoteConnectionEndpoint, inetAddress), exception);
+ }
+
+ public RemoteConnectionException(RemoteConnectionEndpoint.InetSocketAddressCoupling address, Exception exception) {
+ this.address = address;
+ this.exception = exception;
+ }
+
+ public RemoteConnectionEndpoint.InetSocketAddressCoupling 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 RemoteConnectionException from(SARCE remoteConnectionEndpoint, Exception exception) {
+ return new RemoteConnectionException(remoteConnectionEndpoint, remoteConnectionEndpoint.getInetAddress(), exception);
+ }
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/rce/SingleAddressRemoteConnectionEndpoint.java b/smack-core/src/main/java/org/jivesoftware/smack/util/rce/SingleAddressRemoteConnectionEndpoint.java
new file mode 100644
index 000000000..54946d793
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/util/rce/SingleAddressRemoteConnectionEndpoint.java
@@ -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());
+ }
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/rce/package-info.java b/smack-core/src/main/java/org/jivesoftware/smack/util/rce/package-info.java
new file mode 100644
index 000000000..ae0ef2035
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/util/rce/package-info.java
@@ -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;
diff --git a/smack-core/src/test/java/org/jivesoftware/smack/SmackExceptionTest.java b/smack-core/src/test/java/org/jivesoftware/smack/SmackExceptionTest.java
deleted file mode 100644
index 4403f7bf5..000000000
--- a/smack-core/src/test/java/org/jivesoftware/smack/SmackExceptionTest.java
+++ /dev/null
@@ -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 failedAddresses = new LinkedList();
-
- DnsName host = DnsName.from("foo.bar.example");
- InetAddress inetAddress = InetAddress.getByAddress(host.toString(), new byte[] { 0, 0, 0, 0 });
- List 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);
- }
-
-}
diff --git a/smack-core/src/test/java/org/jivesoftware/smack/util/DnsUtilTest.java b/smack-core/src/test/java/org/jivesoftware/smack/util/DnsUtilTest.java
index 626a3d4f4..eef18918f 100644
--- a/smack-core/src/test/java/org/jivesoftware/smack/util/DnsUtilTest.java
+++ b/smack-core/src/test/java/org/jivesoftware/smack/util/DnsUtilTest.java
@@ -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() {
diff --git a/smack-integration-test/build.gradle b/smack-integration-test/build.gradle
index f16e9b76c..f0b380cca 100644
--- a/smack-integration-test/build.gradle
+++ b/smack-integration-test/build.gradle
@@ -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"
diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java
index dd1a7b194..57b85f722 100644
--- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java
+++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2015-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;
diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java
index 5f32f9268..befb94c08 100644
--- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java
@@ -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 connections;
- public AbstractSmackIntegrationTest(SmackIntegrationTestEnvironment> environment) {
+ public AbstractSmackIntegrationTest(SmackIntegrationTestEnvironment environment) {
super(environment);
this.connection = this.conOne = environment.conOne;
this.conTwo = environment.conTwo;
diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackLowLevelIntegrationTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackLowLevelIntegrationTest.java
index cd0b89b48..25435627f 100644
--- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackLowLevelIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackLowLevelIntegrationTest.java
@@ -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;
diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackSpecificLowLevelIntegrationTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackSpecificLowLevelIntegrationTest.java
index 6c9bbe219..492f04c7e 100644
--- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackSpecificLowLevelIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackSpecificLowLevelIntegrationTest.java
@@ -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
extends AbstractSmackLowLevelIntegrationTest {
- private final SmackIntegrationTestEnvironment> environment;
+ private final SmackIntegrationTestEnvironment environment;
protected final Class connectionClass;
private final XmppConnectionDescriptor> connectionDescriptor;
- public AbstractSmackSpecificLowLevelIntegrationTest(SmackIntegrationTestEnvironment> environment,
+ public AbstractSmackSpecificLowLevelIntegrationTest(SmackIntegrationTestEnvironment environment,
Class 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 getConnectionClass() {
diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java
index a6e3d6eda..9b308df3b 100644
--- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java
+++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2015-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 disabledTests;
+ public final String defaultConnectionNickname;
+
+ public final Set enabledConnections;
+
+ public final Set disabledConnections;
+
public final Set 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 enabledTests, Set disabledTests,
- Set 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 disabledTests;
+ private String defaultConnectionNickname;
+
+ private Set enabledConnections;
+
+ private Set disabledConnections;
+
private Set 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 getTestSetFrom(String string) {
- if (string == null) {
+ private static Set split(String input) {
+ return split(input, Function.identity());
+ }
+
+ private static Set split(String input, Function transformer) {
+ if (input == null) {
return null;
}
- String[] stringArray = string.split(",");
- Set res = new HashSet<>(stringArray.length);
- for (String s : stringArray) {
- res.add(getFullTestStringFrom(s));
+
+ String[] inputArray = input.split(",");
+ Set 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;
- }
- return string;
+ private static Set getTestSetFrom(String input) {
+ return split(input, s -> {
+ s = s.trim();
+ if (s.startsWith("smackx.") || s.startsWith("smack.")) {
+ s = "org.jivesoftware." + s;
+ }
+ return s;
+ });
}
+
}
diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestEnvironment.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestEnvironment.java
index 58dae7fb9..938c2f52f 100644
--- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestEnvironment.java
+++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestEnvironment.java
@@ -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 {
+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 connectionManager;
+ public final XmppConnectionManager connectionManager;
- SmackIntegrationTestEnvironment(C conOne, C conTwo, C conThree, String testRunId,
- Configuration configuration, XmppConnectionManager connectionManager) {
+ SmackIntegrationTestEnvironment(AbstractXMPPConnection conOne, AbstractXMPPConnection conTwo, AbstractXMPPConnection conThree, String testRunId,
+ Configuration configuration, XmppConnectionManager connectionManager) {
this.conOne = conOne;
this.conTwo = conTwo;
this.conThree = conThree;
diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java
index 36e9afda2..cc1cf1944 100644
--- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java
+++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java
@@ -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;
@@ -43,7 +44,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 +53,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 +69,7 @@ import org.reflections.scanners.MethodParameterScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
-public class SmackIntegrationTestFramework {
+public class SmackIntegrationTestFramework {
static {
TLSUtils.setDefaultTrustStoreTypeToJksIfRequired();
@@ -80,14 +79,12 @@ public class SmackIntegrationTestFramework {
public static boolean SINTTEST_UNIT_TEST = false;
- private final Class defaultConnectionClass;
-
protected final Configuration config;
protected TestRunResult testRunResult;
- private SmackIntegrationTestEnvironment environment;
- protected XmppConnectionManager connectionManager;
+ private SmackIntegrationTestEnvironment environment;
+ protected XmppConnectionManager connectionManager;
public enum TestType {
Normal,
@@ -100,7 +97,7 @@ public class SmackIntegrationTestFramework {
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Configuration config = Configuration.newConfiguration(args);
- SmackIntegrationTestFramework sinttest = new SmackIntegrationTestFramework<>(config, XMPPTCPConnection.class);
+ SmackIntegrationTestFramework sinttest = new SmackIntegrationTestFramework(config);
TestRunResult testRunResult = sinttest.run();
for (Entry, Throwable> entry : testRunResult.impossibleTestClasses.entrySet()) {
@@ -116,11 +113,9 @@ public class SmackIntegrationTestFramework {
}
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 +141,8 @@ public class SmackIntegrationTestFramework {
System.exit(exitStatus);
}
- public SmackIntegrationTestFramework(Configuration configuration, Class 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 +151,7 @@ public class SmackIntegrationTestFramework {
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 +217,13 @@ public class SmackIntegrationTestFramework {
return testRunResult;
}
- @SuppressWarnings({"unchecked", "Finally"})
+ @SuppressWarnings({"Finally"})
private void runTests(Set> classes)
throws InterruptedException, InstantiationException, IllegalAccessException,
IllegalArgumentException, SmackException, IOException, XMPPException {
+ List tests = new ArrayList<>(classes.size());
+ int numberOfAvailableTests = 0;
+
for (Class extends AbstractSmackIntTest> testClass : classes) {
final String testClassName = testClass.getName();
@@ -260,17 +254,19 @@ public class SmackIntegrationTestFramework {
// - 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 +297,6 @@ public class SmackIntegrationTestFramework {
continue;
}
- testRunResult.numberOfAvailableTestMethods.addAndGet(smackIntegrationTestMethods.size());
-
final AbstractSmackIntTest test;
try {
test = cons.newInstance(environment);
@@ -360,12 +354,14 @@ public class SmackIntegrationTestFramework {
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,106 +372,77 @@ public class SmackIntegrationTestFramework {
continue;
}
- final int detectedTestMethodsCount = smackIntegrationTestMethods.size();
- testRunResult.numberOfPossibleTestMethods.addAndGet(detectedTestMethodsCount);
+ List concreteTests = new ArrayList<>(smackIntegrationTestMethods.size());
- try {
- // Run the @BeforeClass methods (if any)
- Set 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 allBeforeClassMethods = getAllMethods(testClass, withAnnotation(BeforeClass.class));
- allBeforeClassMethods.removeAll(beforeClassMethods);
- if (!allBeforeClassMethods.isEmpty()) {
- throw new IllegalArgumentException("@BeforeClass methods with wrong signature found");
+ for (Method testMethod : smackIntegrationTestMethods) {
+ switch (testType) {
+ case Normal: {
+ ConcreteTest.Executor concreteTestExecutor = () -> testMethod.invoke(test);
+ ConcreteTest concreteTest = new ConcreteTest(testType, testMethod, concreteTestExecutor);
+ concreteTests.add(concreteTest);
}
-
- 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");
- }
-
- for (Method testMethod : smackIntegrationTestMethods) {
- List concreteTests = null;
+ break;
+ case LowLevel:
+ case SpecificLowLevel:
+ LowLevelTestMethod lowLevelTestMethod = new LowLevelTestMethod(testMethod);
switch (testType) {
- case Normal: {
- ConcreteTest.Executor concreteTestExecutor = () -> testMethod.invoke(test);
- ConcreteTest concreteTest = new ConcreteTest(testType, testMethod, concreteTestExecutor);
- concreteTests = Collections.singletonList(concreteTest);
- }
- break;
case LowLevel:
- case SpecificLowLevel:
- LowLevelTestMethod lowLevelTestMethod = new LowLevelTestMethod(testMethod);
- switch (testType) {
- case LowLevel:
- concreteTests = invokeLowLevel(lowLevelTestMethod, (AbstractSmackLowLevelIntegrationTest) test);
- break;
- case SpecificLowLevel: {
- ConcreteTest.Executor concreteTestExecutor = () -> invokeSpecificLowLevel(
- lowLevelTestMethod, (AbstractSmackSpecificLowLevelIntegrationTest>) test);
- ConcreteTest concreteTest = new ConcreteTest(testType, testMethod, concreteTestExecutor);
- concreteTests = Collections.singletonList(concreteTest);
- break;
- }
- default:
- throw new AssertionError();
- }
+ List 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.add(concreteTest);
break;
}
-
- for (ConcreteTest concreteTest : concreteTests) {
- runConcreteTest(concreteTest);
+ default:
+ throw new AssertionError();
}
+ break;
}
}
- finally {
- // Run the @AfterClass method (if any)
- Set 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 allAfterClassMethods = getAllMethods(testClass, withAnnotation(AfterClass.class));
- allAfterClassMethods.removeAll(afterClassMethods);
- if (!allAfterClassMethods.isEmpty()) {
- throw new IllegalArgumentException("@AfterClass methods with wrong signature found");
- }
+ // Instantiate the prepared test early as this will check the before and after class annotations.
+ PreparedTest preparedTest = new PreparedTest(test, concreteTests);
+ tests.add(preparedTest);
- if (afterClassMethods.size() == 1) {
- Method afterClassMethod = afterClassMethods.iterator().next();
- LOGGER.info("Executing @AfterClass method of " + testClass);
- try {
- afterClassMethod.invoke(test);
- }
- catch (InvocationTargetException | IllegalAccessException e) {
- LOGGER.log(Level.SEVERE, "Exception executing @AfterClass method", e);
- }
- catch (IllegalArgumentException e) {
- throw new AssertionError(e);
- }
- }
- else if (afterClassMethods.size() > 1) {
- throw new IllegalArgumentException("Only one @AfterClass method allowed");
- }
- }
+ numberOfAvailableTests += concreteTests.size();
}
+
+ // 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');
+ }
+ }
+ 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 +491,35 @@ public class SmackIntegrationTestFramework {
}
private List invokeLowLevel(LowLevelTestMethod lowLevelTestMethod, AbstractSmackLowLevelIntegrationTest test) {
- Set> 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 resultingConcreteTests = new ArrayList<>(connectionClasses.size());
+ List 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 +528,7 @@ public class SmackIntegrationTestFramework {
return resultingConcreteTests;
}
- private void invokeSpecificLowLevel(LowLevelTestMethod testMethod,
+ private static void invokeSpecificLowLevel(LowLevelTestMethod testMethod,
AbstractSmackSpecificLowLevelIntegrationTest test)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InterruptedException,
SmackException, IOException, XMPPException {
@@ -554,7 +539,7 @@ public class SmackIntegrationTestFramework {
testMethod.invoke(test, connectionClass);
}
- protected SmackIntegrationTestEnvironment prepareEnvironment() throws SmackException,
+ protected SmackIntegrationTestEnvironment prepareEnvironment() throws SmackException,
IOException, XMPPException, InterruptedException, KeyManagementException,
NoSuchAlgorithmException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
return connectionManager.prepareEnvironment();
@@ -576,9 +561,6 @@ public class SmackIntegrationTestFramework {
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 +594,13 @@ public class SmackIntegrationTestFramework {
private final List successfulIntegrationTests = Collections.synchronizedList(new LinkedList());
private final List failedIntegrationTests = Collections.synchronizedList(new LinkedList());
private final List impossibleIntegrationTests = Collections.synchronizedList(new LinkedList());
+
+ // 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 disabledTestClasses = Collections.synchronizedList(new ArrayList<>());
+ private final List disabledTests = Collections.synchronizedList(new ArrayList<>());
+
private final Map, Throwable> impossibleTestClasses = new HashMap<>();
- private final AtomicInteger numberOfAvailableTestMethods = new AtomicInteger();
- private final AtomicInteger numberOfPossibleTestMethods = new AtomicInteger();
TestRunResult() {
}
@@ -624,11 +610,7 @@ public class SmackIntegrationTestFramework {
}
public int getNumberOfAvailableTests() {
- return numberOfAvailableTestMethods.get();
- }
-
- public int getNumberOfPossibleTests() {
- return numberOfPossibleTestMethods.get();
+ return successfulIntegrationTests.size() + failedIntegrationTests.size() + impossibleIntegrationTests.size();
}
public List getSuccessfulTests() {
@@ -648,6 +630,77 @@ public class SmackIntegrationTestFramework {
}
}
+ final class PreparedTest {
+ private final AbstractSmackIntTest test;
+ private final List concreteTests;
+
+ private final Method beforeClassMethod;
+ private final Method afterClassMethod;
+
+ private PreparedTest(AbstractSmackIntTest test, List 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 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 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;
@@ -705,6 +758,50 @@ public class SmackIntegrationTestFramework {
}
}
+ 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 +815,7 @@ public class SmackIntegrationTestFramework {
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 +844,11 @@ public class SmackIntegrationTestFramework {
testMethod.invoke(test, connectionsArray);
}
}
+
+ @Override
+ public String toString() {
+ return testMethod.toString();
+ }
}
private static boolean testMethodParametersIsListOfConnections(Method testMethod) {
diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionDescriptor.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionDescriptor.java
index 29a24aecc..6eaec61e0 100644
--- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionDescriptor.java
+++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionDescriptor.java
@@ -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> {
+public final class XmppConnectionDescriptor<
+ C extends AbstractXMPPConnection,
+ CC extends ConnectionConfiguration,
+ CCB extends ConnectionConfiguration.Builder, CC>
+> {
private final Class connectionClass;
private final Class connectionConfigurationClass;
@@ -38,13 +43,18 @@ public class XmppConnectionDescriptor connectionConstructor;
private final Method builderMethod;
- public XmppConnectionDescriptor(Class connectionClass, Class connectionConfigurationClass)
- throws ClassNotFoundException, NoSuchMethodException, SecurityException {
- this.connectionClass = connectionClass;
- this.connectionConfigurationClass = connectionConfigurationClass;
+ private final Consumer extraBuilder;
- this.connectionConstructor = getConstructor(connectionClass, connectionConfigurationClass);
- this.builderMethod = getBuilderMethod(connectionConfigurationClass);
+ private final String nickname;
+
+ private XmppConnectionDescriptor(Builder 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 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 Constructor getConstructor(Class connectionClass,
Class extends ConnectionConfiguration> connectionConfigurationClass)
throws NoSuchMethodException, SecurityException {
@@ -106,4 +123,47 @@ public class XmppConnectionDescriptor>
+ Builder buildWith(Class connectionClass, Class connectionConfigurationClass) {
+ return buildWith(connectionClass, connectionConfigurationClass, null);
+ }
+
+ public static >
+ Builder buildWith(Class connectionClass, Class connectionConfigurationClass, Class connectionConfigurationBuilderClass) {
+ return new Builder<>(connectionClass, connectionConfigurationClass, connectionConfigurationBuilderClass);
+ }
+
+ public static final class Builder> {
+ private final Class connectionClass;
+ private final Class connectionConfigurationClass;
+
+ private Consumer extraBuilder;
+
+ private String nickname;
+
+ // The connectionConfigurationBuilderClass merely exists for type-checking purposes.
+ @SuppressWarnings("UnusedVariable")
+ private Builder(Class connectionClass, Class connectionConfigurationClass,
+ Class connectionConfigurationBuilderClass) {
+ this.connectionClass = connectionClass;
+ this.connectionConfigurationClass = connectionConfigurationClass;
+
+ nickname = connectionClass.getSimpleName();
+ }
+
+ public Builder applyExtraConfguration(Consumer extraBuilder) {
+ this.extraBuilder = extraBuilder;
+ return this;
+ }
+
+ public Builder withNickname(String nickname) {
+ this.nickname = nickname;
+ return this;
+ }
+
+ public XmppConnectionDescriptor build() throws NoSuchMethodException, SecurityException {
+ return new XmppConnectionDescriptor<>(this);
+ }
+ }
}
diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java
index c576a1948..207ab1c02 100644
--- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java
+++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java
@@ -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 {
+public class XmppConnectionManager {
private static final Logger LOGGER = Logger.getLogger(XmppConnectionManager.class.getName());
- private static final Map, 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>> 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();
- CONNECTION_DESCRIPTORS.put(connectionClass, connectionDescriptor);
+
+ 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;
+ }
+
+ boolean removed = CONNECTION_DESCRIPTORS.removeOne(connectionDescriptor.getConnectionClass(), connectionDescriptor);
+ assert removed;
+ }
+
+ return true;
}
- private final XmppConnectionDescriptor> defaultConnectionDescriptor;
+ private final XmppConnectionDescriptor extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder, ?>> defaultConnectionDescriptor;
- private final Map, XmppConnectionDescriptor extends AbstractXMPPConnection, ? extends ConnectionConfiguration, ? extends ConnectionConfiguration.Builder, ?>>> connectionDescriptors = new HashMap<>(
- CONNECTION_DESCRIPTORS.size());
+ private final Map>> nicknameConnectionDescriptors;
- private final SmackIntegrationTestFramework> sinttestFramework;
+ 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 {
*/
private final List 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>) connectionDescriptors.get(
- defaultConnectionClass);
- if (defaultConnectionDescriptor == null) {
- throw new IllegalArgumentException("Could not find a connection descriptor for " + 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 connection nickname '" + configuredDefaultConnectionNickname + "'");
+ }
+ } else {
+ defaultConnectionDescriptor = DEFAULT_CONNECTION_DESCRIPTOR;
}
switch (sinttestConfiguration.accountRegistration) {
@@ -157,11 +200,11 @@ public class XmppConnectionManager {
}
}
- SmackIntegrationTestEnvironment prepareEnvironment() throws KeyManagementException, NoSuchAlgorithmException,
+ SmackIntegrationTestEnvironment prepareEnvironment() throws KeyManagementException, NoSuchAlgorithmException,
InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
SmackException, IOException, XMPPException, InterruptedException {
prepareMainConnections();
- return new SmackIntegrationTestEnvironment(conOne, conTwo, conThree,
+ return new SmackIntegrationTestEnvironment(conOne, conTwo, conThree,
sinttestFramework.testRunResult.testRunId, sinttestConfiguration, this);
}
@@ -169,9 +212,9 @@ public class XmppConnectionManager {
IllegalAccessException, IllegalArgumentException, InvocationTargetException, SmackException, IOException,
XMPPException, InterruptedException {
final int mainAccountCount = AccountNum.values().length;
- List connections = new ArrayList<>(mainAccountCount);
+ List 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 {
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> getConnectionClasses() {
- return Collections.unmodifiableSet(connectionDescriptors.keySet());
+ public Collection>> getConnectionDescriptors() {
+ return Collections.unmodifiableCollection(nicknameConnectionDescriptors.values());
}
@SuppressWarnings("unchecked")
public XmppConnectionDescriptor> getConnectionDescriptorFor(
Class connectionClass) {
- return (XmppConnectionDescriptor>) connectionDescriptors.get(
+ return (XmppConnectionDescriptor>) connectionDescriptors.getFirst(
connectionClass);
}
@@ -249,7 +292,7 @@ public class XmppConnectionManager {
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 {
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 {
@SuppressWarnings("unchecked")
XmppConnectionDescriptor> connectionDescriptor = (XmppConnectionDescriptor>) 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 {
return connection;
}
- DC constructConnection()
+ AbstractXMPPConnection constructConnection()
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
return constructConnection(defaultConnectionDescriptor);
}
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/ChatTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/ChatTest.java
index ec21761a5..ff058b8e8 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smack/ChatTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/ChatTest.java
@@ -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);
}
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/LoginIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/LoginIntegrationTest.java
index 07a239b00..f43f18f2b 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smack/LoginIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/LoginIntegrationTest.java
@@ -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);
}
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/StreamManagementTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/StreamManagementTest.java
index a26cf8e17..e5e77e4b0 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smack/StreamManagementTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/StreamManagementTest.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2015-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 {
- public StreamManagementTest(SmackIntegrationTestEnvironment> environment) throws Exception {
+ public StreamManagementTest(SmackIntegrationTestEnvironment environment) throws Exception {
super(environment, XMPPTCPConnection.class);
XMPPTCPConnection connection = getSpecificUnconnectedConnection();
connection.connect().login();
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/WaitForClosingStreamElementTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/WaitForClosingStreamElementTest.java
index 8aa741ecb..50ddf0123 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smack/WaitForClosingStreamElementTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/WaitForClosingStreamElementTest.java
@@ -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);
}
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/XmppConnectionIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/XmppConnectionIntegrationTest.java
index 8ca3c8498..29dc527e6 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smack/XmppConnectionIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/XmppConnectionIntegrationTest.java
@@ -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);
}
}
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/chat2/AbstractChatIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/chat2/AbstractChatIntegrationTest.java
index 68db46c5a..3ebf4e852 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smack/chat2/AbstractChatIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/chat2/AbstractChatIntegrationTest.java
@@ -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);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/chat2/IncomingMessageListenerIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/chat2/IncomingMessageListenerIntegrationTest.java
index b031be9d2..ca76ab280 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smack/chat2/IncomingMessageListenerIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/chat2/IncomingMessageListenerIntegrationTest.java
@@ -26,7 +26,7 @@ import org.jxmpp.jid.EntityBareJid;
public class IncomingMessageListenerIntegrationTest extends AbstractChatIntegrationTest {
- public IncomingMessageListenerIntegrationTest(SmackIntegrationTestEnvironment> environment) {
+ public IncomingMessageListenerIntegrationTest(SmackIntegrationTestEnvironment environment) {
super(environment);
}
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/chat2/OutgoingMessageListenerIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/chat2/OutgoingMessageListenerIntegrationTest.java
index c284495fa..6118d2ed2 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smack/chat2/OutgoingMessageListenerIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/chat2/OutgoingMessageListenerIntegrationTest.java
@@ -27,7 +27,7 @@ import org.jxmpp.jid.EntityBareJid;
public class OutgoingMessageListenerIntegrationTest extends AbstractChatIntegrationTest {
- public OutgoingMessageListenerIntegrationTest(SmackIntegrationTestEnvironment> environment) {
+ public OutgoingMessageListenerIntegrationTest(SmackIntegrationTestEnvironment environment) {
super(environment);
}
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/LowLevelRosterIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/LowLevelRosterIntegrationTest.java
index 242491c96..8b73081be 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/LowLevelRosterIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/LowLevelRosterIntegrationTest.java
@@ -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);
}
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java
index beff7eb1b..6f2d0a8fb 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/roster/RosterIntegrationTest.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2015-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);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/tcp/XmppNioTcpConnectionLowLevelIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/tcp/XmppNioTcpConnectionLowLevelIntegrationTest.java
index f42f04ced..5971ceda6 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smack/tcp/XmppNioTcpConnectionLowLevelIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/tcp/XmppNioTcpConnectionLowLevelIntegrationTest.java
@@ -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 {
+public class XmppNioTcpConnectionLowLevelIntegrationTest extends AbstractSmackSpecificLowLevelIntegrationTest {
- 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();
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/caps/EntityCapsTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/caps/EntityCapsTest.java
index c3aa5ede0..8a4946e51 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/caps/EntityCapsTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/caps/EntityCapsTest.java
@@ -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);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/chatstate/ChatStateIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/chatstate/ChatStateIntegrationTest.java
index c236d8aa4..539bdb03d 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/chatstate/ChatStateIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/chatstate/ChatStateIntegrationTest.java
@@ -55,7 +55,7 @@ public class ChatStateIntegrationTest extends AbstractSmackIntegrationTest {
};
- public ChatStateIntegrationTest(SmackIntegrationTestEnvironment> environment) {
+ public ChatStateIntegrationTest(SmackIntegrationTestEnvironment environment) {
super(environment);
}
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferIntegrationTest.java
index 651137694..4a0af1d50 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferIntegrationTest.java
@@ -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);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java
index 6390a2e94..1d2e06914 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java
@@ -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);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTControlIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTControlIntegrationTest.java
index c6f8b275d..4a346479b 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTControlIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTControlIntegrationTest.java
@@ -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);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDataIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDataIntegrationTest.java
index 1fec14577..dc6fc538e 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDataIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDataIntegrationTest.java
@@ -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);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDiscoveryIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDiscoveryIntegrationTest.java
index 1c1c8071d..92c950a86 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDiscoveryIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/IoTDiscoveryIntegrationTest.java
@@ -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);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iqversion/VersionIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iqversion/VersionIntegrationTest.java
index f22194452..577819e11 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iqversion/VersionIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iqversion/VersionIntegrationTest.java
@@ -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);
}
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java
index 0ee3d295b..b0cc99584 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mam/MamIntegrationTest.java
@@ -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);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mood/MoodIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mood/MoodIntegrationTest.java
index 902c2b97f..afac2dd12 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/mood/MoodIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/mood/MoodIntegrationTest.java
@@ -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);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java
index c7651a052..aee855f6a 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2015-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);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatLowLevelIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatLowLevelIntegrationTest.java
index a47eef25f..ba4abd160 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatLowLevelIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatLowLevelIntegrationTest.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2015-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 {
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractOmemoIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractOmemoIntegrationTest.java
index f16ca3d88..4f944d3fa 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractOmemoIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractOmemoIntegrationTest.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2017-2018 Florian Schmaus, Paul Schaub
+ * Copyright 2017-2020 Florian Schmaus, Paul Schaub
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@ import org.igniterealtime.smack.inttest.TestNotPossibleException;
*/
public abstract class AbstractOmemoIntegrationTest extends AbstractSmackIntegrationTest {
- public AbstractOmemoIntegrationTest(SmackIntegrationTestEnvironment> environment) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, TestNotPossibleException {
+ public AbstractOmemoIntegrationTest(SmackIntegrationTestEnvironment environment) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, TestNotPossibleException {
super(environment);
// Test for server support
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractTwoUsersOmemoIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractTwoUsersOmemoIntegrationTest.java
index 68cd5f30c..1e324b77c 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractTwoUsersOmemoIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractTwoUsersOmemoIntegrationTest.java
@@ -40,7 +40,7 @@ public abstract class AbstractTwoUsersOmemoIntegrationTest extends AbstractOmemo
protected OmemoManager alice, bob;
- public AbstractTwoUsersOmemoIntegrationTest(SmackIntegrationTestEnvironment> environment)
+ public AbstractTwoUsersOmemoIntegrationTest(SmackIntegrationTestEnvironment environment)
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
SmackException.NoResponseException, TestNotPossibleException {
super(environment);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/MessageEncryptionIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/MessageEncryptionIntegrationTest.java
index 938696441..26ba5ab08 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/MessageEncryptionIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/MessageEncryptionIntegrationTest.java
@@ -37,7 +37,7 @@ import org.igniterealtime.smack.inttest.TestNotPossibleException;
*/
public class MessageEncryptionIntegrationTest extends AbstractTwoUsersOmemoIntegrationTest {
- public MessageEncryptionIntegrationTest(SmackIntegrationTestEnvironment> environment)
+ public MessageEncryptionIntegrationTest(SmackIntegrationTestEnvironment environment)
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
SmackException.NoResponseException, TestNotPossibleException {
super(environment);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoMamDecryptionTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoMamDecryptionTest.java
index 8a36a2f22..ee39eec90 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoMamDecryptionTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoMamDecryptionTest.java
@@ -41,7 +41,7 @@ import org.igniterealtime.smack.inttest.TestNotPossibleException;
* Then Bob fetches his Mam archive and decrypts the result.
*/
public class OmemoMamDecryptionTest extends AbstractTwoUsersOmemoIntegrationTest {
- public OmemoMamDecryptionTest(SmackIntegrationTestEnvironment> environment)
+ public OmemoMamDecryptionTest(SmackIntegrationTestEnvironment environment)
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
SmackException.NoResponseException, TestNotPossibleException {
super(environment);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java
index dc13bdb8f..bed2b014d 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java
@@ -35,7 +35,7 @@ import org.igniterealtime.smack.inttest.TestNotPossibleException;
public class ReadOnlyDeviceIntegrationTest extends AbstractTwoUsersOmemoIntegrationTest {
- public ReadOnlyDeviceIntegrationTest(SmackIntegrationTestEnvironment> environment) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, TestNotPossibleException {
+ public ReadOnlyDeviceIntegrationTest(SmackIntegrationTestEnvironment environment) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, TestNotPossibleException {
super(environment);
}
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/SessionRenegotiationIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/SessionRenegotiationIntegrationTest.java
index fa863f1be..3b4391ba3 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/SessionRenegotiationIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/SessionRenegotiationIntegrationTest.java
@@ -27,7 +27,7 @@ import org.igniterealtime.smack.inttest.TestNotPossibleException;
public class SessionRenegotiationIntegrationTest extends AbstractTwoUsersOmemoIntegrationTest {
- public SessionRenegotiationIntegrationTest(SmackIntegrationTestEnvironment> environment)
+ public SessionRenegotiationIntegrationTest(SmackIntegrationTestEnvironment environment)
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
SmackException.NoResponseException, TestNotPossibleException {
super(environment);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/AbstractOpenPgpIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/AbstractOpenPgpIntegrationTest.java
index d02d4f3f5..31d613c85 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/AbstractOpenPgpIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/AbstractOpenPgpIntegrationTest.java
@@ -42,7 +42,7 @@ public abstract class AbstractOpenPgpIntegrationTest extends AbstractSmackIntegr
protected final PepManager bobPepManager;
protected final PepManager chloePepManager;
- protected AbstractOpenPgpIntegrationTest(SmackIntegrationTestEnvironment> environment)
+ protected AbstractOpenPgpIntegrationTest(SmackIntegrationTestEnvironment environment)
throws XMPPException.XMPPErrorException, TestNotPossibleException, SmackException.NotConnectedException,
InterruptedException, SmackException.NoResponseException {
super(environment);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/OXSecretKeyBackupIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/OXSecretKeyBackupIntegrationTest.java
index 897dda339..e5e33f39b 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/OXSecretKeyBackupIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox/OXSecretKeyBackupIntegrationTest.java
@@ -98,7 +98,7 @@ public class OXSecretKeyBackupIntegrationTest extends AbstractOpenPgpIntegration
* @throws InterruptedException if the calling thread was interrupted.
* @throws SmackException.NoResponseException if there was no response from the remote entity.
*/
- public OXSecretKeyBackupIntegrationTest(SmackIntegrationTestEnvironment> environment)
+ public OXSecretKeyBackupIntegrationTest(SmackIntegrationTestEnvironment environment)
throws XMPPException.XMPPErrorException, TestNotPossibleException, SmackException.NotConnectedException,
InterruptedException, SmackException.NoResponseException {
super(environment);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingIntegrationTest.java
index 1f68eeb4d..4ca076c2b 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingIntegrationTest.java
@@ -89,7 +89,7 @@ public class OXInstantMessagingIntegrationTest extends AbstractOpenPgpIntegratio
* @throws TestNotPossibleException if the test is not possible due to lacking server support for PEP.
* @throws SmackException.NoResponseException if there was no response from the remote entity.
*/
- public OXInstantMessagingIntegrationTest(SmackIntegrationTestEnvironment> environment)
+ public OXInstantMessagingIntegrationTest(SmackIntegrationTestEnvironment environment)
throws XMPPException.XMPPErrorException, InterruptedException, SmackException.NotConnectedException,
TestNotPossibleException, SmackException.NoResponseException {
super(environment);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ping/PingIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ping/PingIntegrationTest.java
index 1d19c5b4f..6bb73b8c1 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ping/PingIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ping/PingIntegrationTest.java
@@ -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 @@ import org.jxmpp.jid.Jid;
public class PingIntegrationTest extends AbstractSmackIntegrationTest {
- public PingIntegrationTest(SmackIntegrationTestEnvironment> environment) {
+ public PingIntegrationTest(SmackIntegrationTestEnvironment environment) {
super(environment);
}
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/pubsub/PubSubIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/pubsub/PubSubIntegrationTest.java
index cc5811ad0..a0e0bff6c 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/pubsub/PubSubIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/pubsub/PubSubIntegrationTest.java
@@ -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.
@@ -37,7 +37,7 @@ public class PubSubIntegrationTest extends AbstractSmackIntegrationTest {
private final PubSubManager pubSubManagerOne;
- public PubSubIntegrationTest(SmackIntegrationTestEnvironment> environment)
+ public PubSubIntegrationTest(SmackIntegrationTestEnvironment environment)
throws TestNotPossibleException, NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException {
super(environment);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java
index c766bc6e5..b8492529b 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java
@@ -38,7 +38,7 @@ public class UserTuneIntegrationTest extends AbstractSmackIntegrationTest {
private final UserTuneManager utm1;
private final UserTuneManager utm2;
- public UserTuneIntegrationTest(SmackIntegrationTestEnvironment> environment) throws NotLoggedInException {
+ public UserTuneIntegrationTest(SmackIntegrationTestEnvironment environment) throws NotLoggedInException {
super(environment);
utm1 = UserTuneManager.getInstanceFor(conOne);
utm2 = UserTuneManager.getInstanceFor(conTwo);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/xdata/FormTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/xdata/FormTest.java
index cf89a68f2..af49c251a 100644
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/xdata/FormTest.java
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/xdata/FormTest.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2004 Jive Software, 2017-2019 Florian Schmaus.
+ * Copyright 2004 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.
@@ -41,7 +41,7 @@ import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
*/
public class FormTest extends AbstractSmackIntegrationTest {
- public FormTest(SmackIntegrationTestEnvironment> environment) {
+ public FormTest(SmackIntegrationTestEnvironment environment) {
super(environment);
}
diff --git a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/DummySmackIntegrationTestFramework.java b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/DummySmackIntegrationTestFramework.java
index 6617f9b0a..f16e2ba54 100644
--- a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/DummySmackIntegrationTestFramework.java
+++ b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/DummySmackIntegrationTestFramework.java
@@ -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,12 +26,19 @@ import org.jivesoftware.smack.DummyConnection.DummyConnectionConfiguration;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
-public class DummySmackIntegrationTestFramework extends SmackIntegrationTestFramework {
+public class DummySmackIntegrationTestFramework extends SmackIntegrationTestFramework {
+
+ public static final String DUMMY_CONNECTION_NICKNAME = "dummy";
static {
try {
- XmppConnectionManager.addConnectionDescriptor(DummyConnection.class, DummyConnectionConfiguration.class);
- } catch (ClassNotFoundException | NoSuchMethodException | SecurityException e) {
+ XmppConnectionManager.addConnectionDescriptor(
+ XmppConnectionDescriptor
+ .buildWith(DummyConnection.class, DummyConnectionConfiguration.class)
+ .withNickname(DUMMY_CONNECTION_NICKNAME)
+ .build()
+ );
+ } catch (NoSuchMethodException | SecurityException e) {
throw new AssertionError(e);
}
}
@@ -39,15 +46,15 @@ public class DummySmackIntegrationTestFramework extends SmackIntegrationTestFram
public DummySmackIntegrationTestFramework(Configuration configuration) throws KeyManagementException,
InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
NoSuchAlgorithmException, SmackException, IOException, XMPPException, InterruptedException {
- super(configuration, DummyConnection.class);
+ super(configuration);
testRunResult = new TestRunResult();
}
@Override
- protected SmackIntegrationTestEnvironment prepareEnvironment() {
+ protected SmackIntegrationTestEnvironment prepareEnvironment() {
DummyConnection dummyConnection = new DummyConnection();
connectionManager.conOne = connectionManager.conTwo = connectionManager.conThree = dummyConnection;
- return new SmackIntegrationTestEnvironment(dummyConnection, dummyConnection, dummyConnection,
+ return new SmackIntegrationTestEnvironment(dummyConnection, dummyConnection, dummyConnection,
testRunResult.getTestRunId(), config, null);
}
diff --git a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestUnitTestUtil.java b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestUnitTestUtil.java
index de095b69a..c3cd0a0ab 100644
--- a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestUnitTestUtil.java
+++ b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestUnitTestUtil.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2015-2017 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,9 @@ public class SmackIntegrationTestUnitTestUtil {
Configuration configuration = Configuration.builder()
.setService(JidTestUtil.DOMAIN_BARE_JID_1)
.setUsernamesAndPassword("dummy1", "dummy1pass", "dummy2", "dummy2pass", "dummy3", "dummy3pass")
- .addEnabledTest(unitTest).build();
+ .setDefaultConnection(DummySmackIntegrationTestFramework.DUMMY_CONNECTION_NICKNAME)
+ .addEnabledTest(unitTest)
+ .build();
// @formatter:on
try {
return new DummySmackIntegrationTestFramework(configuration);
diff --git a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestXmppConnectionManagerTest.java b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestXmppConnectionManagerTest.java
index 6cd7d0d1c..1709f2a58 100644
--- a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestXmppConnectionManagerTest.java
+++ b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestXmppConnectionManagerTest.java
@@ -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,8 +22,9 @@ import java.lang.reflect.InvocationTargetException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
-import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
-import org.jivesoftware.smack.tcp.XmppNioTcpConnection;
+import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection;
+import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration;
+import org.jivesoftware.smack.tcp.XmppTcpTransportModuleDescriptor;
import org.junit.Test;
import org.jxmpp.stringprep.XmppStringprepException;
@@ -34,11 +35,19 @@ public class SmackIntegrationTestXmppConnectionManagerTest {
public void simpleXmppConnectionDescriptorTest() throws ClassNotFoundException, NoSuchMethodException,
SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
KeyManagementException, NoSuchAlgorithmException, XmppStringprepException, InstantiationException {
- XmppConnectionDescriptor descriptor
- = new XmppConnectionDescriptor<>(XmppNioTcpConnection.class, XMPPTCPConnectionConfiguration.class);
+ XmppConnectionDescriptor<
+ ModularXmppClientToServerConnection,
+ ModularXmppClientToServerConnectionConfiguration,
+ ModularXmppClientToServerConnectionConfiguration.Builder
+ > descriptor = XmppConnectionDescriptor.buildWith(
+ ModularXmppClientToServerConnection.class,
+ ModularXmppClientToServerConnectionConfiguration.class,
+ ModularXmppClientToServerConnectionConfiguration.Builder.class)
+ .applyExtraConfguration(b -> b.removeAllModules().addModule(XmppTcpTransportModuleDescriptor.class))
+ .build();
Configuration sinttestConfiguration = Configuration.builder().setService("example.org").build();
- XmppNioTcpConnection connection = descriptor.construct(sinttestConfiguration);
+ ModularXmppClientToServerConnection connection = descriptor.construct(sinttestConfiguration);
assertEquals("example.org", connection.getXMPPServiceDomain().toString());
}
diff --git a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/unittest/SmackIntegrationTestFrameworkUnitTest.java b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/unittest/SmackIntegrationTestFrameworkUnitTest.java
index a73b432df..9df166d51 100644
--- a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/unittest/SmackIntegrationTestFrameworkUnitTest.java
+++ b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/unittest/SmackIntegrationTestFrameworkUnitTest.java
@@ -72,7 +72,7 @@ public class SmackIntegrationTestFrameworkUnitTest {
public static class ThrowsRuntimeExceptionDummyTest extends AbstractSmackIntegrationTest {
- public ThrowsRuntimeExceptionDummyTest(SmackIntegrationTestEnvironment> environment) {
+ public ThrowsRuntimeExceptionDummyTest(SmackIntegrationTestEnvironment environment) {
super(environment);
}
@@ -103,7 +103,7 @@ public class SmackIntegrationTestFrameworkUnitTest {
public static final String DESCRIPTIVE_TEXT = "I'm not fatal";
- public ThrowsNonFatalExceptionDummyTest(SmackIntegrationTestEnvironment> environment) {
+ public ThrowsNonFatalExceptionDummyTest(SmackIntegrationTestEnvironment environment) {
super(environment);
}
@@ -131,7 +131,7 @@ public class SmackIntegrationTestFrameworkUnitTest {
public static class BeforeAfterClassTest extends AbstractSmackIntegrationTest {
- public BeforeAfterClassTest(SmackIntegrationTestEnvironment> environment) {
+ public BeforeAfterClassTest(SmackIntegrationTestEnvironment environment) {
super(environment);
}
diff --git a/smack-java8-full/build.gradle b/smack-java8-full/build.gradle
new file mode 100644
index 000000000..8bf5dfe6a
--- /dev/null
+++ b/smack-java8-full/build.gradle
@@ -0,0 +1,48 @@
+description = """\
+Full Smack library for Java SE."""
+
+dependencies {
+ api project(':smack-bosh')
+ api project(':smack-debug')
+ api project(':smack-experimental')
+ api project(':smack-extensions')
+ api project(':smack-java7')
+ api project(':smack-legacy')
+ api project(':smack-omemo')
+ api project(':smack-omemo-signal')
+ api project(':smack-openpgp')
+ api project(':smack-resolver-minidns')
+ api project(':smack-resolver-minidns-dox')
+ api project(':smack-tcp')
+
+ testImplementation project(path: ":smack-core", configuration: "testRuntime")
+}
+
+task printModularXmppClientToServerConnectionStateGraph(type: JavaExec) {
+ classpath sourceSets.main.runtimeClasspath
+ main 'org.jivesoftware.smack.full.ModularXmppClientToServerConnectionTool'
+}
+
+task generateModularXmppClientToServerConnectionStateGraph(type: JavaExec) {
+ // TODO: Filter out all files which do not contain the String
+ // StateDescriptor.
+ inputs.files file('..').listFiles().findAll {it.name.endsWith('.java')}
+ outputs.files 'src/javadoc/org/jivesoftware/smack/full/doc-files/ModularXmppClientToServerConnectionStateGraph.dot'
+ classpath sourceSets.main.runtimeClasspath
+ main 'org.jivesoftware.smack.full.ModularXmppClientToServerConnectionTool'
+ args outputs.files
+}
+
+task convertModularXmppClientToServerConnectionStateGraphDotToPng(type: Exec) {
+ dependsOn generateModularXmppClientToServerConnectionStateGraph
+ inputs.files 'src/javadoc/org/jivesoftware/smack/full/doc-files/ModularXmppClientToServerConnectionStateGraph.dot'
+ outputs.files 'src/javadoc/org/jivesoftware/smack/full/doc-files/ModularXmppClientToServerConnectionStateGraph.png'
+
+ executable 'dot'
+ args "-Tpng", "-o", "${outputs.files.first()}", "${inputs.files.first()}"
+}
+
+task cleanGenerateFiles(type: Delete) {
+ delete 'src/javadoc/org/jivesoftware/smack/full/doc-files/ModularXmppClientToServerConnectionStateGraph.dot', 'src/javadoc/org/jivesoftware/smack/full/doc-files/ModularXmppClientToServerConnectionStateGraph.png'
+}
+clean.dependsOn cleanGenerateFiles
diff --git a/smack-java8-full/src/javadoc/org/jivesoftware/smack/full/doc-files/.gitignore b/smack-java8-full/src/javadoc/org/jivesoftware/smack/full/doc-files/.gitignore
new file mode 100644
index 000000000..59fc6dc8e
--- /dev/null
+++ b/smack-java8-full/src/javadoc/org/jivesoftware/smack/full/doc-files/.gitignore
@@ -0,0 +1,2 @@
+/ModularXmppClientToServerConnectionStateGraph.dot
+/ModularXmppClientToServerConnectionStateGraph.png
diff --git a/smack-java8-full/src/main/java/org/jivesoftware/smack/full/ModularXmppClientToServerConnectionTool.java b/smack-java8-full/src/main/java/org/jivesoftware/smack/full/ModularXmppClientToServerConnectionTool.java
new file mode 100644
index 000000000..9455d9e35
--- /dev/null
+++ b/smack-java8-full/src/main/java/org/jivesoftware/smack/full/ModularXmppClientToServerConnectionTool.java
@@ -0,0 +1,71 @@
+/**
+ *
+ * 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.full;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration;
+
+import org.jxmpp.stringprep.XmppStringprepException;
+
+public class ModularXmppClientToServerConnectionTool {
+
+ @SuppressWarnings("DefaultCharset")
+ public static void main(String[] args) throws XmppStringprepException, FileNotFoundException {
+
+ final PrintWriter pw;
+ final boolean breakStateName;
+
+ switch (args.length) {
+ case 0:
+ pw = new PrintWriter(System.out);
+ breakStateName = false;
+ break;
+ case 1:
+ Path outputFilePath = Paths.get(args[0]);
+ File outputFile = outputFilePath.toFile();
+ if (outputFile.exists()) {
+ outputFile.delete();
+ }
+ pw = new PrintWriter(outputFile);
+ breakStateName = true;
+ break;
+ default:
+ throw new IllegalArgumentException("At most one argument allowed");
+ }
+
+ printStateGraph(pw, breakStateName);
+ pw.flush();
+ }
+
+ public static void printStateGraph(PrintWriter pw, boolean breakStateName) throws XmppStringprepException {
+ ModularXmppClientToServerConnectionConfiguration.Builder configurationBuilder = ModularXmppClientToServerConnectionConfiguration.builder()
+ .setUsernameAndPassword("user", "password")
+ .setXmppDomain("example.org");
+
+ ModularXmppClientToServerConnectionConfiguration configuration = configurationBuilder.build();
+
+ configuration.printStateGraphInDotFormat(pw, breakStateName);
+
+ pw.flush();
+ }
+
+}
diff --git a/smack-java8-full/src/main/java/org/jivesoftware/smack/full/package-info.java b/smack-java8-full/src/main/java/org/jivesoftware/smack/full/package-info.java
new file mode 100644
index 000000000..e316eabb0
--- /dev/null
+++ b/smack-java8-full/src/main/java/org/jivesoftware/smack/full/package-info.java
@@ -0,0 +1,28 @@
+/**
+ *
+ * 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.
+ */
+
+/**
+ * The Smack XMPP client library.
+
+ *
Modular XMPP c2s connection
+ *
+ * The graph below shows the current states of the modular xmpp client connection. Only some states are final states,
+ * most states are intermediate states in order to reach a final state.
+ *