1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-11-26 14:02:06 +01:00

Compare commits

..

No commits in common. "11775ed6b089c88a1139d7a7b9fbc78ec73a210e" and "e7372b05eabdac2be483d3956ac164389446dbf3" have entirely different histories.

54 changed files with 173 additions and 1120 deletions

View file

@ -6,11 +6,6 @@
<module name="SuppressionFilter"> <module name="SuppressionFilter">
<property name="file" value="config/suppressions.xml"/> <property name="file" value="config/suppressions.xml"/>
</module> </module>
<module name="SuppressWithPlainTextCommentFilter">
<property name="offCommentFormat" value="CHECKSTYLE\:OFF\:(\w+)"/>
<property name="onCommentFormat" value="CHECKSTYLE\:ON\:(\w+)"/>
<property name="checkFormat" value="$1"/>
</module>
<module name="Header"> <module name="Header">
<property name="headerFile" value="config/${checkstyleLicenseHeader}.txt"/> <property name="headerFile" value="config/${checkstyleLicenseHeader}.txt"/>
<property name="ignoreLines" value="3"/> <property name="ignoreLines" value="3"/>
@ -66,14 +61,6 @@
<property name="format" value="^\s*//[^\s]"/> <property name="format" value="^\s*//[^\s]"/>
<property name="message" value="Comment start ('//') followed by non-space character. You would not continue after a punctuation without a space, would you?"/> <property name="message" value="Comment start ('//') followed by non-space character. You would not continue after a punctuation without a space, would you?"/>
</module> </module>
<!-- Check for synchronized keyword on Manager's static
getInstanceFor() method. Note that if XMPPConnection is every
replaced with something else, then we need to change it here
too. -->
<module name="RegexpSingleline">
<property name="format" value="^\s*public(?!.*synchronized).*getInstanceFor\(XMPPConnection.*$"/>
<property name="message" value="getInstanceFor() should be synchronized"/>
</module>
<module name="JavadocPackage"/> <module name="JavadocPackage"/>
<module name="TreeWalker"> <module name="TreeWalker">
<module name="SuppressionCommentFilter"/> <module name="SuppressionCommentFilter"/>
@ -102,11 +89,6 @@
<property name="message" value="Usage of println"/> <property name="message" value="Usage of println"/>
<property name="ignoreComments" value="true"/> <property name="ignoreComments" value="true"/>
</module> </module>
<module name="RegexpSinglelineJava">
<property name="format" value="Boolean\.valueOf\("/>
<property name="message" value="Usage Boolean.valueOf(), consider using ParserUtils.parseXmlBoolean() instead (if you want to parse xs:boolean values)"/>
<property name="ignoreComments" value="true"/>
</module>
<module name="RegexpSinglelineJava"> <module name="RegexpSinglelineJava">
<property name="format" value="^\t+"/> <property name="format" value="^\t+"/>
<property name="message" value="Indent must not use tab characters. Use space instead."/> <property name="message" value="Indent must not use tab characters. Use space instead."/>

View file

@ -141,74 +141,6 @@ hr {
<div id="pageBody"> <div id="pageBody">
<h2>4.3.3 -- <span style="font-weight: normal;">2019-03-14</span></h2>
<h2> Bug
</h2>
<ul>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-856'>SMACK-856</a>] - Smack fails under JDK 11 because com.sun.jndi.dns.DnsContextFactory is not inaccessible
</li>
</ul>
<h2> Improvement
</h2>
<ul>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-858'>SMACK-858</a>] - Dependency version specifier of jxmpp and MiniDNS include alpha/beta/... versions of the follow up version when Maven is used
</li>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-859'>SMACK-859</a>] - MultiUserChat enter() should reset the timeout of the collector waiting for the final self presence to prevent timeouts for large MUCs
</li>
</ul>
<h2>4.3.2 -- <span style="font-weight: normal;">2019-02-22</span></h2>
<h2> Bug
</h2>
<ul>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-842'>SMACK-842</a>] - The RFC 3920 xml-not-well-formed error condition should be handled in stream error not a stanza error
</li>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-843'>SMACK-843</a>] - ManManager.pagePrevious() pages into the wrong direction
</li>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-844'>SMACK-844</a>] - Check if bounded unacknowledged stanzas queue is full before adding to it to avoid IllegalStateException
</li>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-845'>SMACK-845</a>] - Ensure that IQ response &#39;to&#39; address and ID are set correctly
</li>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-846'>SMACK-846</a>] - XMPPTCPConnection does not wait for stream features after authentication if compression is disabled
</li>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-848'>SMACK-848</a>] - Make MultiUserChat.leave() wait for response
</li>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-850'>SMACK-850</a>] - DeliveryReceiptManager should not send receipts with messages of type &#39;groupchat&#39;
</li>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-855'>SMACK-855</a>] - XMPPTCPConnection sometimes has two writer threads running
</li>
</ul>
<h2> Improvement
</h2>
<ul>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-847'>SMACK-847</a>] - Make TCP socket connection attempt interruptable
</li>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-849'>SMACK-849</a>] - Smack Local SOCKS5 Proxy thread should be marked as daemon thread
</li>
</ul>
<h2>4.3.1 -- <span style="font-weight: normal;">2018-10-14</span></h2>
<h2> Bug
</h2>
<ul>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-833'>SMACK-833</a>] - XMLUtil.prettyFormatXml() throws on some Android devices
</li>
</ul>
<h2> Improvement
</h2>
<ul>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-829'>SMACK-829</a>] - Disconnect BOSH client on shutdown
</li>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-838'>SMACK-838</a>] - FormField.getFirstValue() throws IndexOutOfBoundsException if there are no values
</li>
</ul>
<h2>4.3.0 -- <span style="font-weight: normal;">2018-08-02</span></h2> <h2>4.3.0 -- <span style="font-weight: normal;">2018-08-02</span></h2>
<h2> Bug <h2> Bug

View file

@ -4,7 +4,5 @@ This API is considered beta quality."""
dependencies { dependencies {
compile project(':smack-core') compile project(':smack-core')
// See https://issues.igniterealtime.org/browse/SMACK-858 and compile 'org.igniterealtime.jbosh:jbosh:[0.9,0.10)'
// comment in version.gradle why the specify the version this way.
compile 'org.igniterealtime.jbosh:jbosh:[0.9.1,0.9.999]'
} }

View file

@ -19,8 +19,6 @@ package org.jivesoftware.smack.bosh;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.proxy.ProxyInfo; import org.jivesoftware.smack.proxy.ProxyInfo;
@ -36,7 +34,6 @@ public final class BOSHConfiguration extends ConnectionConfiguration {
private final boolean https; private final boolean https;
private final String file; private final String file;
private Map<String, String> httpHeaders;
private BOSHConfiguration(Builder builder) { private BOSHConfiguration(Builder builder) {
super(builder); super(builder);
@ -52,7 +49,6 @@ public final class BOSHConfiguration extends ConnectionConfiguration {
} else { } else {
file = builder.file; file = builder.file;
} }
httpHeaders = builder.httpHeaders;
} }
public boolean isProxyEnabled() { public boolean isProxyEnabled() {
@ -80,10 +76,6 @@ public final class BOSHConfiguration extends ConnectionConfiguration {
return new URI((https ? "https://" : "http://") + this.host + ":" + this.port + file); return new URI((https ? "https://" : "http://") + this.host + ":" + this.port + file);
} }
public Map<String, String> getHttpHeaders() {
return httpHeaders;
}
public static Builder builder() { public static Builder builder() {
return new Builder(); return new Builder();
} }
@ -91,7 +83,6 @@ public final class BOSHConfiguration extends ConnectionConfiguration {
public static final class Builder extends ConnectionConfiguration.Builder<Builder, BOSHConfiguration> { public static final class Builder extends ConnectionConfiguration.Builder<Builder, BOSHConfiguration> {
private boolean https; private boolean https;
private String file; private String file;
private Map<String, String> httpHeaders = new HashMap<>();
private Builder() { private Builder() {
} }
@ -110,11 +101,6 @@ public final class BOSHConfiguration extends ConnectionConfiguration {
return this; return this;
} }
public Builder addHttpHeader(String name, String value) {
httpHeaders.put(name, value);
return this;
}
@Override @Override
public BOSHConfiguration build() { public BOSHConfiguration build() {
return new BOSHConfiguration(this); return new BOSHConfiguration(this);

View file

@ -22,7 +22,6 @@ import java.io.PipedReader;
import java.io.PipedWriter; import java.io.PipedWriter;
import java.io.StringReader; import java.io.StringReader;
import java.io.Writer; import java.io.Writer;
import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -157,9 +156,6 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
if (config.isProxyEnabled()) { if (config.isProxyEnabled()) {
cfgBuilder.setProxy(config.getProxyAddress(), config.getProxyPort()); cfgBuilder.setProxy(config.getProxyAddress(), config.getProxyPort());
} }
for (Map.Entry<String, String> h : config.getHttpHeaders().entrySet()) {
cfgBuilder.addHttpHeader(h.getKey(), h.getValue());
}
client = BOSHClient.create(cfgBuilder.build()); client = BOSHClient.create(cfgBuilder.build());
client.addBOSHClientConnListener(new BOSHConnectionListener()); client.addBOSHClientConnListener(new BOSHConnectionListener());
@ -267,11 +263,6 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
client = null; client = null;
} }
instantShutdown();
}
@Override
public void instantShutdown() {
setWasAuthenticated(); setWasAuthenticated();
sessionID = null; sessionID = null;
done = true; done = true;

View file

@ -26,7 +26,6 @@ import java.util.LinkedHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
@ -236,7 +235,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
* stanza is send by the server. This is set to true once the last feature stanza has been * stanza is send by the server. This is set to true once the last feature stanza has been
* parsed. * parsed.
*/ */
protected final SynchronizationPoint<SmackException> lastFeaturesReceived = new SynchronizationPoint<>( protected final SynchronizationPoint<Exception> lastFeaturesReceived = new SynchronizationPoint<Exception>(
AbstractXMPPConnection.this, "last stream features received from server"); AbstractXMPPConnection.this, "last stream features received from server");
/** /**
@ -298,18 +297,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
} }
}); });
protected static final AsyncButOrdered<AbstractXMPPConnection> ASYNC_BUT_ORDERED = new AsyncButOrdered<>(); private static final AsyncButOrdered<AbstractXMPPConnection> ASYNC_BUT_ORDERED = new AsyncButOrdered<>();
/**
* An executor which uses {@link #asyncGoLimited(Runnable)} to limit the number of asynchronously processed runnables
* per connection.
*/
private final Executor limitedExcutor = new Executor() {
@Override
public void execute(Runnable runnable) {
asyncGoLimited(runnable);
}
};
/** /**
* The used host to establish the connection to * The used host to establish the connection to
@ -326,9 +314,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
*/ */
protected boolean authenticated = false; protected boolean authenticated = false;
// TODO: Migrate to ZonedDateTime once Smack's minimum required Android SDK level is 26 (8.0, Oreo) or higher.
protected long authenticatedConnectionInitiallyEstablishedTimestamp;
/** /**
* Flag that indicates if the user was authenticated with the server when the connection * Flag that indicates if the user was authenticated with the server when the connection
* to the server was closed (abruptly or not). * to the server was closed (abruptly or not).
@ -396,12 +381,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
@Override @Override
public abstract boolean isUsingCompression(); public abstract boolean isUsingCompression();
protected void initState() {
saslFeatureReceived.init();
lastFeaturesReceived.init();
tlsHandled.init();
}
/** /**
* Establishes a connection to the XMPP server. It basically * Establishes a connection to the XMPP server. It basically
* creates and maintains a connection to the server. * creates and maintains a connection to the server.
@ -420,11 +399,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
throwAlreadyConnectedExceptionIfAppropriate(); throwAlreadyConnectedExceptionIfAppropriate();
// Reset the connection state // Reset the connection state
initState();
saslAuthentication.init(); saslAuthentication.init();
saslFeatureReceived.init();
lastFeaturesReceived.init();
tlsHandled.init();
streamId = null; streamId = null;
try {
// Perform the actual connection to the XMPP service // Perform the actual connection to the XMPP service
connectInternal(); connectInternal();
@ -432,12 +412,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
// from the server and throw an error. First check if we've already negotiated TLS // from the server and throw an error. First check if we've already negotiated TLS
// and are secure, however (features get parsed a second time after TLS is established). // and are secure, however (features get parsed a second time after TLS is established).
if (!isSecureConnection() && getConfiguration().getSecurityMode() == SecurityMode.required) { if (!isSecureConnection() && getConfiguration().getSecurityMode() == SecurityMode.required) {
shutdown();
throw new SecurityRequiredByClientException(); throw new SecurityRequiredByClientException();
} }
} catch (SmackException | IOException | XMPPException | InterruptedException e) {
instantShutdown();
throw e;
}
// Make note of the fact that we're now connected. // Make note of the fact that we're now connected.
connected = true; connected = true;
@ -573,7 +550,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
// - the servers last features stanza has been parsed // - the servers last features stanza has been parsed
// - the timeout occurs // - the timeout occurs
LOGGER.finer("Waiting for last features to be received before continuing with resource binding"); LOGGER.finer("Waiting for last features to be received before continuing with resource binding");
lastFeaturesReceived.checkIfSuccessOrWaitOrThrow(); lastFeaturesReceived.checkIfSuccessOrWait();
if (!hasFeature(Bind.ELEMENT, Bind.NAMESPACE)) { if (!hasFeature(Bind.ELEMENT, Bind.NAMESPACE)) {
@ -605,9 +582,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
} }
protected void afterSuccessfulLogin(final boolean resumed) throws NotConnectedException, InterruptedException { protected void afterSuccessfulLogin(final boolean resumed) throws NotConnectedException, InterruptedException {
if (!resumed) {
authenticatedConnectionInitiallyEstablishedTimestamp = System.currentTimeMillis();
}
// Indicate that we're now authenticated. // Indicate that we're now authenticated.
this.authenticated = true; this.authenticated = true;
@ -789,11 +763,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
*/ */
protected abstract void shutdown(); protected abstract void shutdown();
/**
* Performs an unclean disconnect and shutdown of the connection. Does not send a closing stream stanza.
*/
public abstract void instantShutdown();
@Override @Override
public void addConnectionListener(ConnectionListener connectionListener) { public void addConnectionListener(ConnectionListener connectionListener) {
if (connectionListener == null) { if (connectionListener == null) {
@ -1128,7 +1097,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
if (packet instanceof IQ) { if (packet instanceof IQ) {
final IQ iq = (IQ) packet; final IQ iq = (IQ) packet;
if (iq.isRequestIQ()) { if (iq.isRequestIQ()) {
final IQ iqRequest = iq;
final String key = XmppStringUtils.generateKey(iq.getChildElementName(), iq.getChildElementNamespace()); final String key = XmppStringUtils.generateKey(iq.getChildElementName(), iq.getChildElementNamespace());
IQRequestHandler iqRequestHandler; IQRequestHandler iqRequestHandler;
final IQ.Type type = iq.getType(); final IQ.Type type = iq.getType();
@ -1178,7 +1146,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
executorService = ASYNC_BUT_ORDERED.asExecutorFor(this); executorService = ASYNC_BUT_ORDERED.asExecutorFor(this);
break; break;
case async: case async:
executorService = limitedExcutor; executorService = CACHED_EXECUTOR_SERVICE;
break; break;
} }
final IQRequestHandler finalIqRequestHandler = iqRequestHandler; final IQRequestHandler finalIqRequestHandler = iqRequestHandler;
@ -1194,11 +1162,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
// e.g. to avoid presence leaks. // e.g. to avoid presence leaks.
return; return;
} }
assert (response.getType() == IQ.Type.result || response.getType() == IQ.Type.error);
response.setTo(iqRequest.getFrom());
response.setStanzaId(iqRequest.getStanzaId());
try { try {
sendStanza(response); sendStanza(response);
} }
@ -1228,7 +1191,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
} }
for (final StanzaListener listener : listenersToNotify) { for (final StanzaListener listener : listenersToNotify) {
asyncGoLimited(new Runnable() { asyncGo(new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
@ -1747,18 +1710,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
return lastStanzaReceived; return lastStanzaReceived;
} }
/**
* Get the timestamp when the connection was the first time authenticated, i.e., when the first successful login was
* performed. Note that this value is not reset on disconnect, so it represents the timestamp from the last
* authenticated connection. The value is also not reset on stream resumption.
*
* @return the timestamp or {@code null}.
* @since 4.3.3
*/
public final long getAuthenticatedConnectionInitiallyEstablishedTimestamp() {
return authenticatedConnectionInitiallyEstablishedTimestamp;
}
/** /**
* Install a parsing exception callback, which will be invoked once an exception is encountered while parsing a * Install a parsing exception callback, which will be invoked once an exception is encountered while parsing a
* stanza. * stanza.
@ -1785,75 +1736,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
return getClass().getSimpleName() + '[' + localEndpointString + "] (" + getConnectionCounter() + ')'; return getClass().getSimpleName() + '[' + localEndpointString + "] (" + getConnectionCounter() + ')';
} }
/**
* A queue of deferred runnables that where not executed immediately because {@link #currentAsyncRunnables} reached
* {@link #maxAsyncRunnables}. Note that we use a {@code LinkedList} in order to avoid space blowups in case the
* list ever becomes very big and shrinks again.
*/
private final Queue<Runnable> deferredAsyncRunnables = new LinkedList<>();
private int deferredAsyncRunnablesCount;
private int deferredAsyncRunnablesCountPrevious;
private int maxAsyncRunnables = SmackConfiguration.getDefaultConcurrencyLevelLimit();
private int currentAsyncRunnables;
protected void asyncGoLimited(final Runnable runnable) {
Runnable wrappedRunnable = new Runnable() {
@Override
public void run() {
runnable.run();
synchronized (deferredAsyncRunnables) {
Runnable defferredRunnable = deferredAsyncRunnables.poll();
if (defferredRunnable == null) {
currentAsyncRunnables--;
} else {
deferredAsyncRunnablesCount--;
asyncGo(defferredRunnable);
}
}
}
};
synchronized (deferredAsyncRunnables) {
if (currentAsyncRunnables < maxAsyncRunnables) {
currentAsyncRunnables++;
asyncGo(wrappedRunnable);
} else {
deferredAsyncRunnablesCount++;
deferredAsyncRunnables.add(wrappedRunnable);
}
final int HIGH_WATERMARK = 100;
final int INFORM_WATERMARK = 20;
final int deferredAsyncRunnablesCount = this.deferredAsyncRunnablesCount;
if (deferredAsyncRunnablesCount >= HIGH_WATERMARK
&& deferredAsyncRunnablesCountPrevious < HIGH_WATERMARK) {
LOGGER.log(Level.WARNING, "High watermark of " + HIGH_WATERMARK + " simultaneous executing runnables reached");
} else if (deferredAsyncRunnablesCount >= INFORM_WATERMARK
&& deferredAsyncRunnablesCountPrevious < INFORM_WATERMARK) {
LOGGER.log(Level.INFO, INFORM_WATERMARK + " simultaneous executing runnables reached");
}
deferredAsyncRunnablesCountPrevious = deferredAsyncRunnablesCount;
}
}
public void setMaxAsyncOperations(int maxAsyncOperations) {
if (maxAsyncOperations < 1) {
throw new IllegalArgumentException("Max async operations must be greater than 0");
}
synchronized (deferredAsyncRunnables) {
maxAsyncRunnables = maxAsyncOperations;
}
}
protected static void asyncGo(Runnable runnable) { protected static void asyncGo(Runnable runnable) {
CACHED_EXECUTOR_SERVICE.execute(runnable); CACHED_EXECUTOR_SERVICE.execute(runnable);
} }

View file

@ -55,16 +55,6 @@ public class AsyncButOrdered<K> {
private final Map<K, Boolean> threadActiveMap = new WeakHashMap<>(); private final Map<K, Boolean> threadActiveMap = new WeakHashMap<>();
private final Executor executor;
public AsyncButOrdered() {
this(null);
}
public AsyncButOrdered(Executor executor) {
this.executor = executor;
}
/** /**
* Invoke the given {@link Runnable} asynchronous but ordered in respect to the given key. * Invoke the given {@link Runnable} asynchronous but ordered in respect to the given key.
* *
@ -96,11 +86,7 @@ public class AsyncButOrdered<K> {
if (newHandler) { if (newHandler) {
Handler handler = new Handler(keyQueue, key); Handler handler = new Handler(keyQueue, key);
threadActiveMap.put(key, true); threadActiveMap.put(key, true);
if (executor == null) {
AbstractXMPPConnection.asyncGo(handler); AbstractXMPPConnection.asyncGo(handler);
} else {
executor.execute(handler);
}
} }
} }

View file

@ -18,7 +18,6 @@
package org.jivesoftware.smack; package org.jivesoftware.smack;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.KeyStore; import java.security.KeyStore;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -46,7 +45,6 @@ import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Resourcepart; import org.jxmpp.jid.parts.Resourcepart;
import org.jxmpp.stringprep.XmppStringprepException; import org.jxmpp.stringprep.XmppStringprepException;
import org.minidns.dnsname.DnsName; import org.minidns.dnsname.DnsName;
import org.minidns.util.InetAddressUtil;
/** /**
* Configuration to use while establishing the connection to the server. * Configuration to use while establishing the connection to the server.
@ -173,14 +171,6 @@ public abstract class ConnectionConfiguration {
} }
DnsName getHost() {
return host;
}
InetAddress getHostAddress() {
return hostAddress;
}
/** /**
* Returns the server name of the target server. * Returns the server name of the target server.
* *
@ -652,33 +642,6 @@ public abstract class ConnectionConfiguration {
return getThis(); return getThis();
} }
/**
* Set the host to connect to by either its fully qualified domain name (FQDN) or its IP.
*
* @param fqdnOrIp a CharSequence either representing the FQDN or the IP of the host.
* @return a reference to this builder.
* @see #setHost(DnsName)
* @see #setHostAddress(InetAddress)
* @since 4.3.2
*/
public B setHostAddressByNameOrIp(CharSequence fqdnOrIp) {
String fqdnOrIpString = fqdnOrIp.toString();
if (InetAddressUtil.isIpAddress(fqdnOrIp)) {
InetAddress hostInetAddress;
try {
hostInetAddress = InetAddress.getByName(fqdnOrIpString);
}
catch (UnknownHostException e) {
// Should never happen.
throw new AssertionError(e);
}
setHostAddress(hostInetAddress);
} else {
setHost(fqdnOrIpString);
}
return getThis();
}
public B setPort(int port) { public B setPort(int port) {
if (port < 0 || port > 65535) { if (port < 0 || port > 65535) {
throw new IllegalArgumentException( throw new IllegalArgumentException(

View file

@ -365,19 +365,4 @@ public final class SmackConfiguration {
public static void setUnknownIqRequestReplyMode(UnknownIqRequestReplyMode unknownIqRequestReplyMode) { public static void setUnknownIqRequestReplyMode(UnknownIqRequestReplyMode unknownIqRequestReplyMode) {
SmackConfiguration.unknownIqRequestReplyMode = Objects.requireNonNull(unknownIqRequestReplyMode, "Must set mode"); SmackConfiguration.unknownIqRequestReplyMode = Objects.requireNonNull(unknownIqRequestReplyMode, "Must set mode");
} }
private static final int defaultConcurrencyLevelLimit;
static {
int availableProcessors = Runtime.getRuntime().availableProcessors();
if (availableProcessors < 8) {
defaultConcurrencyLevelLimit = 8;
} else {
defaultConcurrencyLevelLimit = (int) (availableProcessors * 1.1);
}
}
public static int getDefaultConcurrencyLevelLimit() {
return defaultConcurrencyLevelLimit;
}
} }

View file

@ -93,24 +93,13 @@ public class SmackException extends Exception {
return new NoResponseException(sb.toString()); return new NoResponseException(sb.toString());
} }
@Deprecated
// TODO: Remove in Smack 4.4.
public static NoResponseException newWith(XMPPConnection connection, public static NoResponseException newWith(XMPPConnection connection,
StanzaCollector collector) { StanzaCollector collector) {
return newWith(connection, collector.getStanzaFilter()); return newWith(connection, collector.getStanzaFilter());
} }
public static NoResponseException newWith(long timeout,
StanzaCollector collector) {
return newWith(timeout, collector.getStanzaFilter());
}
public static NoResponseException newWith(XMPPConnection connection, StanzaFilter filter) { public static NoResponseException newWith(XMPPConnection connection, StanzaFilter filter) {
return newWith(connection.getReplyTimeout(), filter); final StringBuilder sb = getWaitingFor(connection);
}
public static NoResponseException newWith(long timeout, StanzaFilter filter) {
final StringBuilder sb = getWaitingFor(timeout);
sb.append(" Waited for response using: "); sb.append(" Waited for response using: ");
if (filter != null) { if (filter != null) {
sb.append(filter.toString()); sb.append(filter.toString());
@ -123,10 +112,7 @@ public class SmackException extends Exception {
} }
private static StringBuilder getWaitingFor(XMPPConnection connection) { private static StringBuilder getWaitingFor(XMPPConnection connection) {
return getWaitingFor(connection.getReplyTimeout()); final long replyTimeout = connection.getReplyTimeout();
}
private static StringBuilder getWaitingFor(final long replyTimeout) {
final StringBuilder sb = new StringBuilder(256); final StringBuilder sb = new StringBuilder(256);
sb.append("No response received within reply timeout. Timeout was " sb.append("No response received within reply timeout. Timeout was "
+ replyTimeout + "ms (~" + replyTimeout + "ms (~"
@ -348,16 +334,4 @@ public class SmackException extends Exception {
super("Resource binding was not offered by server"); super("Resource binding was not offered by server");
} }
} }
public static class SmackWrappedException extends SmackException {
/**
*
*/
private static final long serialVersionUID = 1L;
public SmackWrappedException(Exception exception) {
super(exception);
}
}
} }

View file

@ -16,18 +16,11 @@
*/ */
package org.jivesoftware.smack; package org.jivesoftware.smack;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.SocketFactory;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.util.CallbackRecipient; import org.jivesoftware.smack.util.CallbackRecipient;
@ -36,8 +29,6 @@ import org.jivesoftware.smack.util.SuccessCallback;
public abstract class SmackFuture<V, E extends Exception> implements Future<V>, CallbackRecipient<V, E> { public abstract class SmackFuture<V, E extends Exception> implements Future<V>, CallbackRecipient<V, E> {
private static final Logger LOGGER = Logger.getLogger(SmackFuture.class.getName());
private boolean cancelled; private boolean cancelled;
protected V result; protected V result;
@ -103,7 +94,7 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
@Override @Override
public final synchronized V get() throws InterruptedException, ExecutionException { public final synchronized V get() throws InterruptedException, ExecutionException {
while (result == null && exception == null && !cancelled) { while (result == null && exception == null && !cancelled) {
futureWait(); wait();
} }
return getOrThrowExecutionException(); return getOrThrowExecutionException();
@ -111,7 +102,7 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
public final synchronized V getOrThrow() throws E, InterruptedException { public final synchronized V getOrThrow() throws E, InterruptedException {
while (result == null && exception == null && !cancelled) { while (result == null && exception == null && !cancelled) {
futureWait(); wait();
} }
if (exception != null) { if (exception != null) {
@ -133,7 +124,7 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
while (result != null && exception != null) { while (result != null && exception != null) {
final long waitTimeRemaining = deadline - System.currentTimeMillis(); final long waitTimeRemaining = deadline - System.currentTimeMillis();
if (waitTimeRemaining > 0) { if (waitTimeRemaining > 0) {
futureWait(waitTimeRemaining); wait(waitTimeRemaining);
} }
} }
@ -171,15 +162,6 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
} }
} }
protected final void futureWait() throws InterruptedException {
futureWait(0);
}
@SuppressWarnings("WaitNotInLoop")
protected void futureWait(long timeout) throws InterruptedException {
wait(timeout);
}
public static class InternalSmackFuture<V, E extends Exception> extends SmackFuture<V, E> { public static class InternalSmackFuture<V, E extends Exception> extends SmackFuture<V, E> {
public final synchronized void setResult(V result) { public final synchronized void setResult(V result) {
this.result = result; this.result = result;
@ -196,64 +178,6 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
} }
} }
public static class SocketFuture extends InternalSmackFuture<Socket, IOException> {
private final Socket socket;
private final Object wasInterruptedLock = new Object();
private boolean wasInterrupted;
public SocketFuture(SocketFactory socketFactory) throws IOException {
socket = socketFactory.createSocket();
}
@Override
protected void futureWait(long timeout) throws InterruptedException {
try {
super.futureWait(timeout);
} catch (InterruptedException interruptedException) {
synchronized (wasInterruptedLock) {
wasInterrupted = true;
if (!socket.isClosed()) {
closeSocket();
}
}
throw interruptedException;
}
}
public void connectAsync(final SocketAddress socketAddress, final int timeout) {
AbstractXMPPConnection.asyncGo(new Runnable() {
@Override
public void run() {
try {
socket.connect(socketAddress, timeout);
}
catch (IOException e) {
setException(e);
return;
}
synchronized (wasInterruptedLock) {
if (wasInterrupted) {
closeSocket();
return;
}
}
setResult(socket);
}
});
}
private void closeSocket() {
try {
socket.close();
}
catch (IOException ioException) {
LOGGER.log(Level.WARNING, "Could not close socket", ioException);
}
}
}
public abstract static class InternalProcessStanzaSmackFuture<V, E extends Exception> extends InternalSmackFuture<V, E> public abstract static class InternalProcessStanzaSmackFuture<V, E extends Exception> extends InternalSmackFuture<V, E>
implements StanzaListener, ExceptionCallback<E> { implements StanzaListener, ExceptionCallback<E> {

View file

@ -262,7 +262,7 @@ public class StanzaCollector {
if (!connection.isConnected()) { if (!connection.isConnected()) {
throw new NotConnectedException(connection, packetFilter); throw new NotConnectedException(connection, packetFilter);
} }
throw NoResponseException.newWith(timeout, this); throw NoResponseException.newWith(connection, this);
} }
XMPPErrorException.ifHasErrorThenThrow(result); XMPPErrorException.ifHasErrorThenThrow(result);

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright © 2014-2019 Florian Schmaus * Copyright © 2014-2015 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,7 +22,6 @@ import java.util.concurrent.locks.Lock;
import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.SmackException.SmackWrappedException;
import org.jivesoftware.smack.packet.Nonza; import org.jivesoftware.smack.packet.Nonza;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.TopLevelStreamElement; import org.jivesoftware.smack.packet.TopLevelStreamElement;
@ -38,7 +37,6 @@ public class SynchronizationPoint<E extends Exception> {
// same memory synchronization effects as synchronization block enter and leave. // same memory synchronization effects as synchronization block enter and leave.
private State state; private State state;
private E failureException; private E failureException;
private SmackWrappedException smackWrappedExcpetion;
/** /**
* Construct a new synchronization point for the given connection. * Construct a new synchronization point for the given connection.
@ -61,7 +59,6 @@ public class SynchronizationPoint<E extends Exception> {
connectionLock.lock(); connectionLock.lock();
state = State.Initial; state = State.Initial;
failureException = null; failureException = null;
smackWrappedExcpetion = null;
connectionLock.unlock(); connectionLock.unlock();
} }
@ -74,7 +71,7 @@ public class SynchronizationPoint<E extends Exception> {
* @throws InterruptedException if the connection is interrupted. * @throws InterruptedException if the connection is interrupted.
* @return <code>null</code> if synchronization point was successful, or the failure Exception. * @return <code>null</code> if synchronization point was successful, or the failure Exception.
*/ */
public Exception sendAndWaitForResponse(TopLevelStreamElement request) throws NoResponseException, public E sendAndWaitForResponse(TopLevelStreamElement request) throws NoResponseException,
NotConnectedException, InterruptedException { NotConnectedException, InterruptedException {
assert (state == State.Initial); assert (state == State.Initial);
connectionLock.lock(); connectionLock.lock();
@ -106,14 +103,15 @@ public class SynchronizationPoint<E extends Exception> {
* @throws NoResponseException if no response was received. * @throws NoResponseException if no response was received.
* @throws NotConnectedException if the connection is not connected. * @throws NotConnectedException if the connection is not connected.
* @throws InterruptedException if the connection is interrupted. * @throws InterruptedException if the connection is interrupted.
* @throws SmackWrappedException in case of a wrapped exception;
*/ */
public void sendAndWaitForResponseOrThrow(Nonza request) throws E, NoResponseException, public void sendAndWaitForResponseOrThrow(Nonza request) throws E, NoResponseException,
NotConnectedException, InterruptedException, SmackWrappedException { NotConnectedException, InterruptedException {
sendAndWaitForResponse(request); sendAndWaitForResponse(request);
switch (state) { switch (state) {
case Failure: case Failure:
throwException(); if (failureException != null) {
throw failureException;
}
break; break;
default: default:
// Success, do nothing // Success, do nothing
@ -125,12 +123,11 @@ public class SynchronizationPoint<E extends Exception> {
* @throws NoResponseException if there was no response marking the synchronization point as success or failed. * @throws NoResponseException if there was no response marking the synchronization point as success or failed.
* @throws E if there was a failure * @throws E if there was a failure
* @throws InterruptedException if the connection is interrupted. * @throws InterruptedException if the connection is interrupted.
* @throws SmackWrappedException in case of a wrapped exception;
*/ */
public void checkIfSuccessOrWaitOrThrow() throws NoResponseException, E, InterruptedException, SmackWrappedException { public void checkIfSuccessOrWaitOrThrow() throws NoResponseException, E, InterruptedException {
checkIfSuccessOrWait(); checkIfSuccessOrWait();
if (state == State.Failure) { if (state == State.Failure) {
throwException(); throw failureException;
} }
} }
@ -140,7 +137,7 @@ public class SynchronizationPoint<E extends Exception> {
* @throws InterruptedException * @throws InterruptedException
* @return <code>null</code> if synchronization point was successful, or the failure Exception. * @return <code>null</code> if synchronization point was successful, or the failure Exception.
*/ */
public Exception checkIfSuccessOrWait() throws NoResponseException, InterruptedException { public E checkIfSuccessOrWait() throws NoResponseException, InterruptedException {
connectionLock.lock(); connectionLock.lock();
try { try {
switch (state) { switch (state) {
@ -148,7 +145,7 @@ public class SynchronizationPoint<E extends Exception> {
case Success: case Success:
return null; return null;
case Failure: case Failure:
return getException(); return failureException;
default: default:
// Do nothing // Do nothing
break; break;
@ -201,24 +198,6 @@ public class SynchronizationPoint<E extends Exception> {
} }
} }
/**
* Report this synchronization point as failed because of the given exception. The {@code failureException} must be set.
*
* @param exception the exception causing this synchronization point to fail.
*/
public void reportGenericFailure(SmackWrappedException exception) {
assert exception != null;
connectionLock.lock();
try {
state = State.Failure;
this.smackWrappedExcpetion = exception;
condition.signalAll();
}
finally {
connectionLock.unlock();
}
}
/** /**
* Check if this synchronization point was successful. * Check if this synchronization point was successful.
* *
@ -234,16 +213,6 @@ public class SynchronizationPoint<E extends Exception> {
} }
} }
public boolean isNotInInitialState() {
connectionLock.lock();
try {
return state != State.Initial;
}
finally {
connectionLock.unlock();
}
}
/** /**
* Check if this synchronization point has its request already sent. * Check if this synchronization point has its request already sent.
* *
@ -287,20 +256,6 @@ public class SynchronizationPoint<E extends Exception> {
} }
} }
private Exception getException() {
if (failureException != null) {
return failureException;
}
return smackWrappedExcpetion;
}
private void throwException() throws E, SmackWrappedException {
if (failureException != null) {
throw failureException;
}
throw smackWrappedExcpetion;
}
/** /**
* Check for a response and throw a {@link NoResponseException} if there was none. * Check for a response and throw a {@link NoResponseException} if there was none.
* <p> * <p>
@ -309,7 +264,7 @@ public class SynchronizationPoint<E extends Exception> {
* @return <code>true</code> if synchronization point was successful, <code>false</code> on failure. * @return <code>true</code> if synchronization point was successful, <code>false</code> on failure.
* @throws NoResponseException * @throws NoResponseException
*/ */
private Exception checkForResponse() throws NoResponseException { private E checkForResponse() throws NoResponseException {
switch (state) { switch (state) {
case Initial: case Initial:
case NoResponse: case NoResponse:
@ -318,7 +273,7 @@ public class SynchronizationPoint<E extends Exception> {
case Success: case Success:
return null; return null;
case Failure: case Failure:
return getException(); return failureException;
default: default:
throw new AssertionError("Unknown state " + state); throw new AssertionError("Unknown state " + state);
} }

View file

@ -198,12 +198,6 @@ public class StanzaError extends AbstractError implements ExtensionElement {
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder("XMPPError: "); StringBuilder sb = new StringBuilder("XMPPError: ");
sb.append(condition.toString()).append(" - ").append(type.toString()); sb.append(condition.toString()).append(" - ").append(type.toString());
String descriptiveText = getDescriptiveText();
if (descriptiveText != null) {
sb.append(" [").append(descriptiveText).append(']');
}
if (errorGenerator != null) { if (errorGenerator != null) {
sb.append(". Generated by ").append(errorGenerator); sb.append(". Generated by ").append(errorGenerator);
} }
@ -391,6 +385,11 @@ public class StanzaError extends AbstractError implements ExtensionElement {
} }
public static Condition fromString(String string) { public static Condition fromString(String string) {
// Backwards compatibility for older implementations still using RFC 3920. RFC 6120
// changed 'xml-not-well-formed' to 'not-well-formed'.
if ("xml-not-well-formed".equals(string)) {
string = "not-well-formed";
}
string = string.replace('-', '_'); string = string.replace('-', '_');
Condition condition = null; Condition condition = null;
try { try {

View file

@ -186,11 +186,6 @@ public class StreamError extends AbstractError implements Nonza {
} }
public static Condition fromString(String string) { public static Condition fromString(String string) {
// Backwards compatibility for older implementations still using RFC 3920. RFC 6120
// changed 'xml-not-well-formed' to 'not-well-formed'.
if ("xml-not-well-formed".equals(string)) {
string = "not-well-formed";
}
string = string.replace('-', '_'); string = string.replace('-', '_');
Condition condition = null; Condition condition = null;
try { try {

View file

@ -123,9 +123,7 @@ public class IntrospectionProvider{
case "java.lang.String": case "java.lang.String":
return value; return value;
case "boolean": case "boolean":
// CHECKSTYLE:OFF
return Boolean.valueOf(value); return Boolean.valueOf(value);
// CHECKSTYLE:ON
case "int": case "int":
return Integer.valueOf(value); return Integer.valueOf(value);
case "long": case "long":

View file

@ -58,7 +58,7 @@ class HTTPProxySocketConnection implements ProxySocketConnection {
proxyLine = "\r\nProxy-Authorization: Basic " + Base64.encode(username + ":" + password); proxyLine = "\r\nProxy-Authorization: Basic " + Base64.encode(username + ":" + password);
} }
socket.getOutputStream().write((hostport + " HTTP/1.1\r\nHost: " socket.getOutputStream().write((hostport + " HTTP/1.1\r\nHost: "
+ host + ":" + port + proxyLine + "\r\n\r\n").getBytes("UTF-8")); + hostport + proxyLine + "\r\n\r\n").getBytes("UTF-8"));
InputStream in = socket.getInputStream(); InputStream in = socket.getInputStream();
StringBuilder got = new StringBuilder(100); StringBuilder got = new StringBuilder(100);
@ -115,8 +115,7 @@ class HTTPProxySocketConnection implements ProxySocketConnection {
int code = Integer.parseInt(m.group(1)); int code = Integer.parseInt(m.group(1));
if (code != HttpURLConnection.HTTP_OK) { if (code != HttpURLConnection.HTTP_OK) {
throw new ProxyException(ProxyInfo.ProxyType.HTTP, throw new ProxyException(ProxyInfo.ProxyType.HTTP);
"Error code in proxy response: " + code);
} }
} }

View file

@ -129,27 +129,6 @@ public class ParserUtils {
return Resourcepart.from(resourcepartString); return Resourcepart.from(resourcepartString);
} }
/**
* Prase a string to a boolean value as per "xs:boolean". Valid input strings are "true", "1" for true, and "false", "0" for false.
*
* @param booleanString the input string.
* @return the boolean representation of the input string
* @throws IllegalArgumentException if the input string is not valid.
* @since 4.3.2
*/
public static boolean parseXmlBoolean(String booleanString) {
switch (booleanString) {
case "true":
case "1":
return true;
case "false":
case "0":
return false;
default:
throw new IllegalArgumentException(booleanString + " is not a valid boolean string");
}
}
/** /**
* Get the boolean value of an argument. * Get the boolean value of an argument.
* *
@ -162,7 +141,7 @@ public class ParserUtils {
if (valueString == null) if (valueString == null)
return null; return null;
valueString = valueString.toLowerCase(Locale.US); valueString = valueString.toLowerCase(Locale.US);
return parseXmlBoolean(valueString); return valueString.equals("true") || valueString.equals("0");
} }
public static boolean getBooleanAttribute(XmlPullParser parser, String name, public static boolean getBooleanAttribute(XmlPullParser parser, String name,

View file

@ -35,11 +35,7 @@ public class XmlUtil {
private static final TransformerFactory transformerFactory = TransformerFactory.newInstance(); private static final TransformerFactory transformerFactory = TransformerFactory.newInstance();
static { static {
try {
transformerFactory.setAttribute("indent-number", 2); transformerFactory.setAttribute("indent-number", 2);
} catch (IllegalArgumentException e) {
LOGGER.log(Level.INFO, "XML TransformerFactory does not support indent-number attribute", e);
}
} }
public static String prettyFormatXml(CharSequence xml) { public static String prettyFormatXml(CharSequence xml) {

View file

@ -1,79 +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 static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.jxmpp.jid.JidTestUtil;
public class ConnectionConfigurationTest {
@Test
public void setIp() {
DummyConnectionConfiguration.Builder builder = newUnitTestBuilder();
final String ip = "192.168.0.1";
builder.setHostAddressByNameOrIp(ip);
DummyConnectionConfiguration connectionConfiguration = builder.build();
assertEquals('/' + ip, connectionConfiguration.getHostAddress().toString());
}
@Test
public void setFqdn() {
DummyConnectionConfiguration.Builder builder = newUnitTestBuilder();
final String fqdn = "foo.example.org";
builder.setHostAddressByNameOrIp(fqdn);
DummyConnectionConfiguration connectionConfiguration = builder.build();
assertEquals(fqdn, connectionConfiguration.getHost().toString());
}
private static DummyConnectionConfiguration.Builder newUnitTestBuilder() {
DummyConnectionConfiguration.Builder builder = DummyConnectionConfiguration.builder();
builder.setXmppDomain(JidTestUtil.DOMAIN_BARE_JID_1);
return builder;
}
private static final class DummyConnectionConfiguration extends ConnectionConfiguration {
protected DummyConnectionConfiguration(Builder builder) {
super(builder);
}
public static Builder builder() {
return new Builder();
}
private static final class Builder
extends ConnectionConfiguration.Builder<Builder, DummyConnectionConfiguration> {
@Override
public DummyConnectionConfiguration build() {
return new DummyConnectionConfiguration(this);
}
@Override
protected Builder getThis() {
return this;
}
}
}
}

View file

@ -100,11 +100,6 @@ public class DummyConnection extends AbstractXMPPConnection {
callConnectionClosedListener(); callConnectionClosedListener();
} }
@Override
public void instantShutdown() {
shutdown();
}
@Override @Override
public boolean isSecureConnection() { public boolean isSecureConnection() {
return false; return false;
@ -231,5 +226,4 @@ public class DummyConnection extends AbstractXMPPConnection {
} }
} }
} }
} }

View file

@ -104,22 +104,4 @@ public class StreamErrorTest {
assertNotNull(appSpecificElement); assertNotNull(appSpecificElement);
} }
@Test
public void testStreamErrorXmlNotWellFormed() {
StreamError error = null;
final String xml =
// Usually the stream:stream element has more attributes (to, version, ...)
// We omit those, since they are not relevant for testing
"<stream:stream from='im.example.com' id='++TR84Sm6A3hnt3Q065SnAbbk3Y=' xmlns:stream='http://etherx.jabber.org/streams'>" +
"<stream:error><xml-not-well-formed xmlns='urn:ietf:params:xml:ns:xmpp-streams'/></stream:error>" +
"</stream:stream>";
try {
XmlPullParser parser = PacketParserUtils.getParserFor(xml, "error");
error = PacketParserUtils.parseStreamError(parser);
} catch (Exception e) {
fail(e.getMessage());
}
assertNotNull(error);
assertEquals(Condition.not_well_formed, error.getCondition());
}
} }

View file

@ -16,8 +16,6 @@
*/ */
package org.jivesoftware.smackx.hoxt.provider; package org.jivesoftware.smackx.hoxt.provider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smackx.hoxt.packet.HttpMethod; import org.jivesoftware.smackx.hoxt.packet.HttpMethod;
import org.jivesoftware.smackx.hoxt.packet.HttpOverXmppReq; import org.jivesoftware.smackx.hoxt.packet.HttpOverXmppReq;
@ -49,13 +47,13 @@ public class HttpOverXmppReqProvider extends AbstractHttpOverXmppProvider<HttpOv
String jingleStr = parser.getAttributeValue("", AbstractHttpOverXmppProvider.ELEMENT_JINGLE); String jingleStr = parser.getAttributeValue("", AbstractHttpOverXmppProvider.ELEMENT_JINGLE);
if (sipubStr != null) { if (sipubStr != null) {
builder.setSipub(ParserUtils.parseXmlBoolean(sipubStr)); builder.setSipub(Boolean.valueOf(sipubStr));
} }
if (ibbStr != null) { if (ibbStr != null) {
builder.setIbb(ParserUtils.parseXmlBoolean(ibbStr)); builder.setIbb(Boolean.valueOf(ibbStr));
} }
if (jingleStr != null) { if (jingleStr != null) {
builder.setJingle(ParserUtils.parseXmlBoolean(jingleStr)); builder.setJingle(Boolean.valueOf(jingleStr));
} }
String maxChunkSize = parser.getAttributeValue("", ATTRIBUTE_MAX_CHUNK_SIZE); String maxChunkSize = parser.getAttributeValue("", ATTRIBUTE_MAX_CHUNK_SIZE);

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright © 2016-2018 Florian Schmaus * Copyright © 2016 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,8 +16,6 @@
*/ */
package org.jivesoftware.smackx.iot.control.element; package org.jivesoftware.smackx.iot.control.element;
import org.jivesoftware.smack.util.ParserUtils;
public class SetBoolData extends SetData { public class SetBoolData extends SetData {
public SetBoolData(String name, boolean value) { public SetBoolData(String name, boolean value) {
@ -33,7 +31,7 @@ public class SetBoolData extends SetData {
public Boolean getBooleanValue() { public Boolean getBooleanValue() {
if (booleanCache != null) { if (booleanCache != null) {
booleanCache = ParserUtils.parseXmlBoolean(getValue()); booleanCache = Boolean.valueOf(getValue());
} }
return booleanCache; return booleanCache;
} }

View file

@ -32,7 +32,7 @@ public final class JingleFileTransferManager extends Manager {
super(connection); super(connection);
} }
public static synchronized JingleFileTransferManager getInstanceFor(XMPPConnection connection) { public static JingleFileTransferManager getInstanceFor(XMPPConnection connection) {
JingleFileTransferManager manager = INSTANCES.get(connection); JingleFileTransferManager manager = INSTANCES.get(connection);
if (manager == null) { if (manager == null) {
manager = new JingleFileTransferManager(connection); manager = new JingleFileTransferManager(connection);

View file

@ -114,8 +114,8 @@ import org.jxmpp.jid.Jid;
* <pre> * <pre>
* {@code * {@code
* MamQueryArgs mamQueryArgs = MamQueryArgs.builder() * MamQueryArgs mamQueryArgs = MamQueryArgs.builder()
* .limitResultsToJid(jid) * .withJid(jid)
* .setResultPageSizeTo(10) * .setResultPageSize(10)
* .queryLastPage() * .queryLastPage()
* .build(); * .build();
* MamQuery mamQuery = mamManager.queryArchive(mamQueryArgs); * MamQuery mamQuery = mamManager.queryArchive(mamQueryArgs);
@ -178,9 +178,7 @@ public final class MamManager extends Manager {
* @param connection the XMPP connection to get the archive for. * @param connection the XMPP connection to get the archive for.
* @return the instance of MamManager. * @return the instance of MamManager.
*/ */
// CHECKSTYLE:OFF:RegexpSingleline
public static MamManager getInstanceFor(XMPPConnection connection) { public static MamManager getInstanceFor(XMPPConnection connection) {
// CHECKSTYLE:ON:RegexpSingleline
return getInstanceFor(connection, (Jid) null); return getInstanceFor(connection, (Jid) null);
} }
@ -997,7 +995,7 @@ public final class MamManager extends Manager {
public List<Message> pagePrevious(int count) throws NoResponseException, XMPPErrorException, public List<Message> pagePrevious(int count) throws NoResponseException, XMPPErrorException,
NotConnectedException, NotLoggedInException, InterruptedException { NotConnectedException, NotLoggedInException, InterruptedException {
RSMSet previousResultRsmSet = getPreviousRsmSet(); RSMSet previousResultRsmSet = getPreviousRsmSet();
RSMSet requestRsmSet = new RSMSet(count, previousResultRsmSet.getFirst(), RSMSet.PageDirection.before); RSMSet requestRsmSet = new RSMSet(count, previousResultRsmSet.getLast(), RSMSet.PageDirection.before);
return page(requestRsmSet); return page(requestRsmSet);
} }

View file

@ -51,7 +51,7 @@ public final class ReferenceManager extends Manager {
* @param connection xmpp connection * @param connection xmpp connection
* @return reference manager instance * @return reference manager instance
*/ */
public static synchronized ReferenceManager getInstanceFor(XMPPConnection connection) { public static ReferenceManager getInstanceFor(XMPPConnection connection) {
ReferenceManager manager = INSTANCES.get(connection); ReferenceManager manager = INSTANCES.get(connection);
if (manager == null) { if (manager == null) {
manager = new ReferenceManager(connection); manager = new ReferenceManager(connection);

View file

@ -27,6 +27,7 @@ import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.MessageTypeFilter; import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.NotFilter; import org.jivesoftware.smack.filter.NotFilter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.ToTypeFilter; import org.jivesoftware.smack.filter.ToTypeFilter;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
@ -45,12 +46,13 @@ public final class StableUniqueStanzaIdManager extends Manager {
MessageTypeFilter.NORMAL_OR_CHAT_OR_HEADLINE, MessageTypeFilter.NORMAL_OR_CHAT_OR_HEADLINE,
ToTypeFilter.ENTITY_FULL_OR_BARE_JID); ToTypeFilter.ENTITY_FULL_OR_BARE_JID);
private static final StanzaFilter ORIGIN_ID_FILTER = new StanzaExtensionFilter(OriginIdElement.ELEMENT, NAMESPACE);
// Listener for outgoing stanzas that adds origin-ids to outgoing stanzas. // Listener for outgoing stanzas that adds origin-ids to outgoing stanzas.
private static final StanzaListener ADD_ORIGIN_ID_INTERCEPTOR = new StanzaListener() { private final StanzaListener stanzaListener = new StanzaListener() {
@Override @Override
public void processStanza(Stanza stanza) { public void processStanza(Stanza stanza) {
Message message = (Message) stanza; OriginIdElement.addOriginId((Message) stanza);
OriginIdElement.addOriginId(message);
} }
}; };
@ -78,7 +80,7 @@ public final class StableUniqueStanzaIdManager extends Manager {
* @param connection xmpp-connection * @param connection xmpp-connection
* @return manager instance for the connection * @return manager instance for the connection
*/ */
public static synchronized StableUniqueStanzaIdManager getInstanceFor(XMPPConnection connection) { public static StableUniqueStanzaIdManager getInstanceFor(XMPPConnection connection) {
StableUniqueStanzaIdManager manager = INSTANCES.get(connection); StableUniqueStanzaIdManager manager = INSTANCES.get(connection);
if (manager == null) { if (manager == null) {
manager = new StableUniqueStanzaIdManager(connection); manager = new StableUniqueStanzaIdManager(connection);
@ -93,7 +95,7 @@ public final class StableUniqueStanzaIdManager extends Manager {
public synchronized void enable() { public synchronized void enable() {
ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE); ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE);
StanzaFilter filter = new AndFilter(OUTGOING_FILTER, new NotFilter(OUTGOING_FILTER)); StanzaFilter filter = new AndFilter(OUTGOING_FILTER, new NotFilter(OUTGOING_FILTER));
connection().addStanzaInterceptor(ADD_ORIGIN_ID_INTERCEPTOR, filter); connection().addStanzaInterceptor(stanzaListener, filter);
} }
/** /**
@ -101,7 +103,7 @@ public final class StableUniqueStanzaIdManager extends Manager {
*/ */
public synchronized void disable() { public synchronized void disable() {
ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE); ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE);
connection().removeStanzaInterceptor(ADD_ORIGIN_ID_INTERCEPTOR); connection().removeStanzaInterceptor(stanzaListener);
} }
/** /**

View file

@ -23,11 +23,7 @@ import org.xmlpull.v1.XmlPullParser;
public class OriginIdProvider extends ExtensionElementProvider<OriginIdElement> { public class OriginIdProvider extends ExtensionElementProvider<OriginIdElement> {
public static final OriginIdProvider INSTANCE = new OriginIdProvider(); public static final OriginIdProvider TEST_INSTANCE = new OriginIdProvider();
// TODO: Remove in Smack 4.4.
@Deprecated
public static final OriginIdProvider TEST_INSTANCE = INSTANCE;
@Override @Override
public OriginIdElement parse(XmlPullParser parser, int initialDepth) throws Exception { public OriginIdElement parse(XmlPullParser parser, int initialDepth) throws Exception {

View file

@ -23,11 +23,7 @@ import org.xmlpull.v1.XmlPullParser;
public class StanzaIdProvider extends ExtensionElementProvider<StanzaIdElement> { public class StanzaIdProvider extends ExtensionElementProvider<StanzaIdElement> {
public static final StanzaIdProvider INSTANCE = new StanzaIdProvider(); public static StanzaIdProvider TEST_INSTANCE = new StanzaIdProvider();
// TODO: Remove in Smack 4.4.
@Deprecated
public static final StanzaIdProvider TEST_INSTANCE = INSTANCE;
@Override @Override
public StanzaIdElement parse(XmlPullParser parser, int initialDepth) throws Exception { public StanzaIdElement parse(XmlPullParser parser, int initialDepth) throws Exception {

View file

@ -61,7 +61,7 @@ public final class SpoilerManager extends Manager {
* @param connection xmpp connection * @param connection xmpp connection
* @return SpoilerManager * @return SpoilerManager
*/ */
public static synchronized SpoilerManager getInstanceFor(XMPPConnection connection) { public static SpoilerManager getInstanceFor(XMPPConnection connection) {
SpoilerManager manager = INSTANCES.get(connection); SpoilerManager manager = INSTANCES.get(connection);
if (manager == null) { if (manager == null) {
manager = new SpoilerManager(connection); manager = new SpoilerManager(connection);

View file

@ -25,7 +25,6 @@ import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.test.util.SmackTestSuite; import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smackx.sid.element.OriginIdElement; import org.jivesoftware.smackx.sid.element.OriginIdElement;
import org.jivesoftware.smackx.sid.element.StanzaIdElement; import org.jivesoftware.smackx.sid.element.StanzaIdElement;
import org.jivesoftware.smackx.sid.provider.OriginIdProvider; import org.jivesoftware.smackx.sid.provider.OriginIdProvider;
@ -43,7 +42,7 @@ public class StableUniqueStanzaIdTest extends SmackTestSuite {
assertEquals("alice@wonderland.lit", element.getBy()); assertEquals("alice@wonderland.lit", element.getBy());
assertXMLEqual(xml, element.toXML(null).toString()); assertXMLEqual(xml, element.toXML(null).toString());
StanzaIdElement parsed = StanzaIdProvider.INSTANCE.parse(TestUtils.getParser(xml)); StanzaIdElement parsed = StanzaIdProvider.TEST_INSTANCE.parse(TestUtils.getParser(xml));
assertEquals(element.getId(), parsed.getId()); assertEquals(element.getId(), parsed.getId());
assertEquals(element.getBy(), parsed.getBy()); assertEquals(element.getBy(), parsed.getBy());
} }
@ -55,7 +54,7 @@ public class StableUniqueStanzaIdTest extends SmackTestSuite {
assertEquals("de305d54-75b4-431b-adb2-eb6b9e546013", element.getId()); assertEquals("de305d54-75b4-431b-adb2-eb6b9e546013", element.getId());
assertXMLEqual(xml, element.toXML(null).toString()); assertXMLEqual(xml, element.toXML(null).toString());
OriginIdElement parsed = OriginIdProvider.INSTANCE.parse(TestUtils.getParser(xml)); OriginIdElement parsed = OriginIdProvider.TEST_INSTANCE.parse(TestUtils.getParser(xml));
assertEquals(element.getId(), parsed.getId()); assertEquals(element.getId(), parsed.getId());
} }
@ -82,17 +81,4 @@ public class StableUniqueStanzaIdTest extends SmackTestSuite {
assertTrue(StanzaIdElement.hasStanzaId(message)); assertTrue(StanzaIdElement.hasStanzaId(message));
assertEquals(stanzaId, StanzaIdElement.getStanzaId(message)); assertEquals(stanzaId, StanzaIdElement.getStanzaId(message));
} }
@Test
public void testMultipleUssidExtensions() throws Exception {
String message = "<message xmlns='jabber:client' from='e4aec989-3e20-4846-83bf-f50df89b5d07@muclight.example.com/user1@example.com' to='user1@example.com' id='6b71fe3a-3cb2-489c-9c8e-b6879761d15e' type='groupchat'>" +
"<body>Test message</body>" +
"<markable xmlns='urn:xmpp:chat-markers:0'/>" +
"<stanza-id by='e4aec989-3e20-4846-83bf-f50df89b5d07@muclight.example.com' id='B0KK24ETVC81' xmlns='urn:xmpp:sid:0'/>" +
"<stanza-id by='user1@example.com' id='B0KK24EV89G1' xmlns='urn:xmpp:sid:0'/>" +
"</message>";
Message messageStanza = PacketParserUtils.parseStanza(message);
assertTrue(StanzaIdElement.hasStanzaId(messageStanza));
}
} }

View file

@ -269,12 +269,12 @@ public class Bookmarks implements PrivateData {
private static BookmarkedConference getConferenceStorage(XmlPullParser parser) throws XmlPullParserException, IOException { private static BookmarkedConference getConferenceStorage(XmlPullParser parser) throws XmlPullParserException, IOException {
String name = parser.getAttributeValue("", "name"); String name = parser.getAttributeValue("", "name");
boolean autojoin = ParserUtils.getBooleanAttribute(parser, "autojoin", false); String autojoin = parser.getAttributeValue("", "autojoin");
EntityBareJid jid = ParserUtils.getBareJidAttribute(parser); EntityBareJid jid = ParserUtils.getBareJidAttribute(parser);
BookmarkedConference conf = new BookmarkedConference(jid); BookmarkedConference conf = new BookmarkedConference(jid);
conf.setName(name); conf.setName(name);
conf.setAutoJoin(autojoin); conf.setAutoJoin(Boolean.valueOf(autojoin));
// Check for nickname // Check for nickname
boolean done = false; boolean done = false;

View file

@ -211,7 +211,6 @@ public final class Socks5Proxy {
if (this.serverSocket != null) { if (this.serverSocket != null) {
this.serverThread = new Thread(this.serverProcess); this.serverThread = new Thread(this.serverProcess);
this.serverThread.setName("Smack Local SOCKS5 Proxy"); this.serverThread.setName("Smack Local SOCKS5 Proxy");
this.serverThread.setDaemon(true);
this.serverThread.start(); this.serverThread.start();
} }
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2003-2007 Jive Software, 2018-2019 Florian Schmaus. * Copyright 2003-2007 Jive Software, 2018 Florian Schmaus.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -227,7 +227,7 @@ public final class ServiceDiscoveryManager extends Manager {
/** /**
* Returns the type of client that will be returned when asked for the client identity in a * Returns the type of client that will be returned when asked for the client identity in a
* disco request. The valid types are defined by the category client. Follow this link to learn * disco request. The valid types are defined by the category client. Follow this link to learn
* the possible types: <a href="https://xmpp.org/registrar/disco-categories.html">XMPP Registry for Service Discovery Identities</a> * the possible types: <a href="http://xmpp.org/registrar/disco-categories.html#client">Jabber::Registrar</a>.
* *
* @return the type of client that will be returned when asked for the client identity in a * @return the type of client that will be returned when asked for the client identity in a
* disco request. * disco request.
@ -271,8 +271,8 @@ public final class ServiceDiscoveryManager extends Manager {
*/ */
public Set<DiscoverInfo.Identity> getIdentities() { public Set<DiscoverInfo.Identity> getIdentities() {
Set<Identity> res = new HashSet<>(identities); Set<Identity> res = new HashSet<>(identities);
// Add the main identity that must exist // Add the default identity that must exist
res.add(identity); res.add(defaultIdentity);
return Collections.unmodifiableSet(res); return Collections.unmodifiableSet(res);
} }

View file

@ -263,7 +263,7 @@ public class DiscoverInfo extends IQ implements TypedCloneable<DiscoverInfo> {
* Represents the identity of a given XMPP entity. An entity may have many identities but all * Represents the identity of a given XMPP entity. An entity may have many identities but all
* the identities SHOULD have the same name.<p> * the identities SHOULD have the same name.<p>
* *
* Refer to <a href="https://xmpp.org/registrar/disco-categories.html">XMPP Registry for Service Discovery Identities</a> * Refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a>
* in order to get the official registry of values for the <i>category</i> and <i>type</i> * in order to get the official registry of values for the <i>category</i> and <i>type</i>
* attributes. * attributes.
* *
@ -327,7 +327,7 @@ public class DiscoverInfo extends IQ implements TypedCloneable<DiscoverInfo> {
/** /**
* Returns the entity's category. To get the official registry of values for the * Returns the entity's category. To get the official registry of values for the
* 'category' attribute refer to <a href="https://xmpp.org/registrar/disco-categories.html">XMPP Registry for Service Discovery Identities</a>. * 'category' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a>
* *
* @return the entity's category. * @return the entity's category.
*/ */
@ -346,7 +346,7 @@ public class DiscoverInfo extends IQ implements TypedCloneable<DiscoverInfo> {
/** /**
* Returns the entity's type. To get the official registry of values for the * Returns the entity's type. To get the official registry of values for the
* 'type' attribute refer to <a href="https://xmpp.org/registrar/disco-categories.html">XMPP Registry for Service Discovery Identities</a>. * 'type' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a>
* *
* @return the entity's type. * @return the entity's type.
*/ */

View file

@ -48,7 +48,7 @@ public final class JingleTransportMethodManager extends Manager {
super(connection); super(connection);
} }
public static synchronized JingleTransportMethodManager getInstanceFor(XMPPConnection connection) { public static JingleTransportMethodManager getInstanceFor(XMPPConnection connection) {
JingleTransportMethodManager manager = INSTANCES.get(connection); JingleTransportMethodManager manager = INSTANCES.get(connection);
if (manager == null) { if (manager == null) {
manager = new JingleTransportMethodManager(connection); manager = new JingleTransportMethodManager(connection);

View file

@ -38,7 +38,7 @@ public final class JingleIBBTransportManager extends JingleTransportManager<Jing
JingleContentProviderManager.addJingleContentTransportProvider(getNamespace(), new JingleIBBTransportProvider()); JingleContentProviderManager.addJingleContentTransportProvider(getNamespace(), new JingleIBBTransportProvider());
} }
public static synchronized JingleIBBTransportManager getInstanceFor(XMPPConnection connection) { public static JingleIBBTransportManager getInstanceFor(XMPPConnection connection) {
JingleIBBTransportManager manager = INSTANCES.get(connection); JingleIBBTransportManager manager = INSTANCES.get(connection);
if (manager == null) { if (manager == null) {
manager = new JingleIBBTransportManager(connection); manager = new JingleIBBTransportManager(connection);

View file

@ -63,7 +63,7 @@ public final class JingleS5BTransportManager extends JingleTransportManager<Jing
JingleContentProviderManager.addJingleContentTransportProvider(getNamespace(), new JingleS5BTransportProvider()); JingleContentProviderManager.addJingleContentTransportProvider(getNamespace(), new JingleS5BTransportProvider());
} }
public static synchronized JingleS5BTransportManager getInstanceFor(XMPPConnection connection) { public static JingleS5BTransportManager getInstanceFor(XMPPConnection connection) {
JingleS5BTransportManager manager = INSTANCES.get(connection); JingleS5BTransportManager manager = INSTANCES.get(connection);
if (manager == null) { if (manager == null) {
manager = new JingleS5BTransportManager(connection); manager = new JingleS5BTransportManager(connection);

View file

@ -92,9 +92,7 @@ public class JivePropertiesExtensionProvider extends ExtensionElementProvider<Ji
value = Double.valueOf(valueText); value = Double.valueOf(valueText);
} }
else if ("boolean".equals(type)) { else if ("boolean".equals(type)) {
// CHECKSTYLE:OFF
value = Boolean.valueOf(valueText); value = Boolean.valueOf(valueText);
// CHECKSTYLE:ON
} }
else if ("string".equals(type)) { else if ("string".equals(type)) {
value = valueText; value = valueText;

View file

@ -205,6 +205,7 @@ public class MultiUserChat {
final Presence presence = (Presence) packet; final Presence presence = (Presence) packet;
final EntityFullJid from = presence.getFrom().asEntityFullJidIfPossible(); final EntityFullJid from = presence.getFrom().asEntityFullJidIfPossible();
if (from == null) { if (from == null) {
LOGGER.warning("Presence not from a full JID: " + presence.getFrom());
return; return;
} }
final EntityFullJid myRoomJID = myRoomJid; final EntityFullJid myRoomJID = myRoomJid;
@ -340,9 +341,8 @@ public class MultiUserChat {
// Setup the messageListeners and presenceListeners *before* the join presence is send. // Setup the messageListeners and presenceListeners *before* the join presence is send.
connection.addSyncStanzaListener(messageListener, fromRoomGroupchatFilter); connection.addSyncStanzaListener(messageListener, fromRoomGroupchatFilter);
StanzaFilter presenceFromRoomFilter = new AndFilter(fromRoomFilter, connection.addSyncStanzaListener(presenceListener, new AndFilter(fromRoomFilter,
StanzaTypeFilter.PRESENCE); StanzaTypeFilter.PRESENCE));
connection.addSyncStanzaListener(presenceListener, presenceFromRoomFilter);
// @formatter:off // @formatter:off
connection.addSyncStanzaListener(subjectListener, connection.addSyncStanzaListener(subjectListener,
new AndFilter(fromRoomFilter, new AndFilter(fromRoomFilter,
@ -371,27 +371,15 @@ public class MultiUserChat {
) )
); );
// @formatter:on // @formatter:on
StanzaCollector presenceStanzaCollector = null;
Presence presence; Presence presence;
try { try {
// This stanza collector will collect the final self presence from the MUC, which also signals that we have successful entered the MUC. presence = connection.createStanzaCollectorAndSend(responseFilter, joinPresence).nextResultOrThrow(conf.getTimeout());
StanzaCollector selfPresenceCollector = connection.createStanzaCollectorAndSend(responseFilter, joinPresence);
StanzaCollector.Configuration presenceStanzaCollectorConfguration = StanzaCollector.newConfiguration().setCollectorToReset(
selfPresenceCollector).setStanzaFilter(presenceFromRoomFilter);
// This stanza collector is used to reset the timeout of the selfPresenceCollector.
presenceStanzaCollector = connection.createStanzaCollector(presenceStanzaCollectorConfguration);
presence = selfPresenceCollector.nextResultOrThrow(conf.getTimeout());
} }
catch (NotConnectedException | InterruptedException | NoResponseException | XMPPErrorException e) { catch (NotConnectedException | InterruptedException | NoResponseException | XMPPErrorException e) {
// Ensure that all callbacks are removed if there is an exception // Ensure that all callbacks are removed if there is an exception
removeConnectionCallbacks(); removeConnectionCallbacks();
throw e; throw e;
} }
finally {
if (presenceStanzaCollector != null) {
presenceStanzaCollector.cancel();
}
}
// This presence must be send from a full JID. We use the resourcepart of this JID as nick, since the room may // This presence must be send from a full JID. We use the resourcepart of this JID as nick, since the room may
// performed roomnick rewriting // performed roomnick rewriting
@ -747,12 +735,7 @@ public class MultiUserChat {
// If we've already joined the room, leave it before joining under a new // If we've already joined the room, leave it before joining under a new
// nickname. // nickname.
if (joined) { if (joined) {
try { leave();
leaveSync();
}
catch (XMPPErrorException | NoResponseException | MucNotJoinedException e) {
LOGGER.log(Level.WARNING, "Could not leave MUC prior joining, assuming we are not joined", e);
}
} }
enter(mucEnterConfiguration); enter(mucEnterConfiguration);
} }
@ -793,50 +776,6 @@ public class MultiUserChat {
connection.sendStanza(leavePresence); connection.sendStanza(leavePresence);
} }
/**
* Leave the chat room.
*
* @return the leave presence as reflected by the MUC.
* @throws NotConnectedException
* @throws InterruptedException
* @throws XMPPErrorException
* @throws NoResponseException
* @throws MucNotJoinedException
*/
public synchronized Presence leaveSync()
throws NotConnectedException, InterruptedException, NoResponseException, XMPPErrorException, MucNotJoinedException {
// Note that this method is intentionally not guarded by
// "if (!joined) return" because it should be always be possible to leave the room in case the instance's
// state does not reflect the actual state.
// Reset occupant information first so that we are assume that we left the room even if sendStanza() would
// throw.
userHasLeft();
final EntityFullJid myRoomJid = this.myRoomJid;
if (myRoomJid == null) {
throw new MucNotJoinedException(this);
}
// We leave a room by sending a presence packet where the "to"
// field is in the form "roomName@service/nickname"
Presence leavePresence = new Presence(Presence.Type.unavailable);
leavePresence.setTo(myRoomJid);
StanzaFilter reflectedLeavePresenceFilter = new AndFilter(
StanzaTypeFilter.PRESENCE,
new StanzaIdFilter(leavePresence),
new OrFilter(
new AndFilter(FromMatchesFilter.createFull(myRoomJid), PresenceTypeFilter.UNAVAILABLE, MUCUserStatusCodeFilter.STATUS_110_PRESENCE_TO_SELF),
new AndFilter(fromRoomFilter, PresenceTypeFilter.ERROR)
)
);
Presence reflectedLeavePresence = connection.createStanzaCollectorAndSend(reflectedLeavePresenceFilter, leavePresence).nextResultOrThrow();
return reflectedLeavePresence;
}
/** /**
* Get a {@link MucConfigFormManager} to configure this room. * Get a {@link MucConfigFormManager} to configure this room.
* <p> * <p>

View file

@ -19,7 +19,6 @@ package org.jivesoftware.smackx.muc;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -393,46 +392,19 @@ public final class MultiUserChatManager extends Manager {
* @throws NotConnectedException * @throws NotConnectedException
* @throws InterruptedException * @throws InterruptedException
* @throws NotAMucServiceException * @throws NotAMucServiceException
* @deprecated use {@link #getRoomsHostedBy(DomainBareJid)} instead.
*/ */
@Deprecated
// TODO: Remove in Smack 4.4.
public List<HostedRoom> getHostedRooms(DomainBareJid serviceName) throws NoResponseException, XMPPErrorException, public List<HostedRoom> getHostedRooms(DomainBareJid serviceName) throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException, NotAMucServiceException { NotConnectedException, InterruptedException, NotAMucServiceException {
Map<EntityBareJid, HostedRoom> hostedRooms = getRoomsHostedBy(serviceName);
return new ArrayList<>(hostedRooms.values());
}
/**
* Returns a Map of HostedRooms where each HostedRoom has the XMPP address of the room and the room's name.
* Once discovered the rooms hosted by a chat service it is possible to discover more detailed room information or
* join the room.
*
* @param serviceName the service that is hosting the rooms to discover.
* @return a map from the room's address to its HostedRoom information.
* @throws XMPPErrorException
* @throws NoResponseException
* @throws NotConnectedException
* @throws InterruptedException
* @throws NotAMucServiceException
* @since 4.3.1
*/
public Map<EntityBareJid, HostedRoom> getRoomsHostedBy(DomainBareJid serviceName) throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException, NotAMucServiceException {
if (!providesMucService(serviceName)) { if (!providesMucService(serviceName)) {
throw new NotAMucServiceException(serviceName); throw new NotAMucServiceException(serviceName);
} }
ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection()); ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection());
DiscoverItems discoverItems = discoManager.discoverItems(serviceName); DiscoverItems discoverItems = discoManager.discoverItems(serviceName);
List<DiscoverItems.Item> items = discoverItems.getItems(); List<DiscoverItems.Item> items = discoverItems.getItems();
List<HostedRoom> answer = new ArrayList<HostedRoom>(items.size());
Map<EntityBareJid, HostedRoom> answer = new HashMap<>(items.size());
for (DiscoverItems.Item item : items) { for (DiscoverItems.Item item : items) {
HostedRoom hostedRoom = new HostedRoom(item); answer.add(new HostedRoom(item));
HostedRoom previousRoom = answer.put(hostedRoom.getJid(), hostedRoom);
assert previousRoom == null;
} }
return answer; return answer;
} }

View file

@ -207,8 +207,7 @@ public class RoomInfo {
FormField subjectmodField = form.getField("muc#roominfo_subjectmod"); FormField subjectmodField = form.getField("muc#roominfo_subjectmod");
if (subjectmodField != null && !subjectmodField.getValues().isEmpty()) { if (subjectmodField != null && !subjectmodField.getValues().isEmpty()) {
String firstValue = subjectmodField.getFirstValue(); subjectmod = Boolean.valueOf(subjectmodField.getFirstValue());
subjectmod = ("true".equals(firstValue) || "1".equals(firstValue));
} }
FormField urlField = form.getField("muc#roominfo_logs"); FormField urlField = form.getField("muc#roominfo_logs");

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2013-2014 Georg Lukas, 2015-2019 Florian Schmaus * Copyright 2013-2014 Georg Lukas, 2015 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -74,16 +74,8 @@ import org.jxmpp.jid.Jid;
*/ */
public final class DeliveryReceiptManager extends Manager { public final class DeliveryReceiptManager extends Manager {
/** private static final StanzaFilter MESSAGES_WITH_DELIVERY_RECEIPT_REQUEST = new AndFilter(StanzaTypeFilter.MESSAGE,
* Filters all non-error messages with receipt requests. new StanzaExtensionFilter(new DeliveryReceiptRequest()));
* See <a href="https://xmpp.org/extensions/xep-0184.html#when">XEP-0184 § 5.</a> "A sender could request receipts
* on any non-error content message (chat, groupchat, headline, or normal)"
*/
private static final StanzaFilter NON_ERROR_GROUPCHAT_MESSAGES_WITH_DELIVERY_RECEIPT_REQUEST = new AndFilter(
StanzaTypeFilter.MESSAGE,
new StanzaExtensionFilter(new DeliveryReceiptRequest()),
new NotFilter(MessageTypeFilter.ERROR));
private static final StanzaFilter MESSAGES_WITH_DELIVERY_RECEIPT = new AndFilter(StanzaTypeFilter.MESSAGE, private static final StanzaFilter MESSAGES_WITH_DELIVERY_RECEIPT = new AndFilter(StanzaTypeFilter.MESSAGE,
new StanzaExtensionFilter(DeliveryReceipt.ELEMENT, DeliveryReceipt.NAMESPACE)); new StanzaExtensionFilter(DeliveryReceipt.ELEMENT, DeliveryReceipt.NAMESPACE));
@ -183,7 +175,7 @@ public final class DeliveryReceiptManager extends Manager {
} }
connection.sendStanza(ack); connection.sendStanza(ack);
} }
}, NON_ERROR_GROUPCHAT_MESSAGES_WITH_DELIVERY_RECEIPT_REQUEST); }, MESSAGES_WITH_DELIVERY_RECEIPT_REQUEST);
} }
/** /**

View file

@ -261,14 +261,12 @@ public class FormField implements NamedElement {
*/ */
public String getFirstValue() { public String getFirstValue() {
CharSequence firstValue; CharSequence firstValue;
synchronized (values) { synchronized (values) {
if (values.isEmpty()) {
return null;
}
firstValue = values.get(0); firstValue = values.get(0);
} }
if (firstValue == null) {
return null;
}
return firstValue.toString(); return firstValue.toString();
} }

View file

@ -951,7 +951,8 @@ public final class Roster extends Manager {
// This is used in case no available presence is found // This is used in case no available presence is found
Presence unavailable = null; Presence unavailable = null;
for (Presence p : userPresences.values()) { for (Resourcepart resource : userPresences.keySet()) {
Presence p = userPresences.get(resource);
if (!p.isAvailable()) { if (!p.isAvailable()) {
unavailable = p; unavailable = p;
continue; continue;
@ -1471,29 +1472,7 @@ public final class Roster extends Manager {
final Presence presence = (Presence) packet; final Presence presence = (Presence) packet;
final Jid from = presence.getFrom(); final Jid from = presence.getFrom();
final BareJid key; final BareJid key = from != null ? from.asBareJid() : null;
if (from != null) {
key = from.asBareJid();
} else {
XMPPConnection connection = connection();
if (connection == null) {
LOGGER.finest("Connection was null while trying to handle exotic presence stanza: " + presence);
return;
}
// Assume the presence come "from the users account on the server" since no from was set (RFC 6120 §
// 8.1.2.1 4.). Note that getUser() may return null, but should never return null in this case as where
// connected.
EntityFullJid myJid = connection.getUser();
if (myJid == null) {
LOGGER.info(
"Connection had no local address in Roster's presence listener."
+ " Possibly we received a presence without from before being authenticated."
+ " Presence: " + presence);
return;
}
LOGGER.info("Exotic presence stanza without from received: " + presence);
key = myJid.asBareJid();
}
asyncButOrdered.performAsyncButOrdered(key, new Runnable() { asyncButOrdered.performAsyncButOrdered(key, new Runnable() {
@Override @Override

View file

@ -16,9 +16,6 @@
*/ */
package org.jivesoftware.smackx.muc; package org.jivesoftware.smackx.muc;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.List; import java.util.List;
import org.jivesoftware.smack.MessageListener; import org.jivesoftware.smack.MessageListener;
@ -26,12 +23,8 @@ import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.muc.MultiUserChat.MucCreateConfigFormHandle; import org.jivesoftware.smackx.muc.MultiUserChat.MucCreateConfigFormHandle;
import org.jivesoftware.smackx.muc.MultiUserChatException.MucNotJoinedException;
import org.jivesoftware.smackx.muc.MultiUserChatException.NotAMucServiceException;
import org.jivesoftware.smackx.muc.packet.MUCUser;
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTest;
@ -43,7 +36,6 @@ import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Localpart; import org.jxmpp.jid.parts.Localpart;
import org.jxmpp.jid.parts.Resourcepart; import org.jxmpp.jid.parts.Resourcepart;
import org.jxmpp.stringprep.XmppStringprepException;
public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest { public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest {
@ -69,24 +61,6 @@ public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest {
} }
} }
@SmackIntegrationTest
public void mucJoinLeaveTest() throws XmppStringprepException, NotAMucServiceException, NoResponseException,
XMPPErrorException, NotConnectedException, InterruptedException, MucNotJoinedException {
EntityBareJid mucAddress = JidCreate.entityBareFrom(Localpart.from("smack-inttest-join-leave-" + randomString),
mucService.getDomain());
MultiUserChat muc = mucManagerOne.getMultiUserChat(mucAddress);
muc.join(Resourcepart.from("nick-one"));
Presence reflectedLeavePresence = muc.leaveSync();
MUCUser mucUser = MUCUser.from(reflectedLeavePresence);
assertNotNull(mucUser);
assertTrue(mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110));
}
@SmackIntegrationTest @SmackIntegrationTest
public void mucTest() throws Exception { public void mucTest() throws Exception {
EntityBareJid mucAddress = JidCreate.entityBareFrom(Localpart.from("smack-inttest-" + randomString), mucService.getDomain()); EntityBareJid mucAddress = JidCreate.entityBareFrom(Localpart.from("smack-inttest-" + randomString), mucService.getDomain());

View file

@ -620,7 +620,7 @@ public class JingleSession extends JingleNegotiator implements MediaReceivedList
* A XMPP connection * A XMPP connection
* @return a Jingle session * @return a Jingle session
*/ */
public static synchronized JingleSession getInstanceFor(XMPPConnection con) { public static JingleSession getInstanceFor(XMPPConnection con) {
if (con == null) { if (con == null) {
throw new IllegalArgumentException("XMPPConnection cannot be null"); throw new IllegalArgumentException("XMPPConnection cannot be null");
} }

View file

@ -104,9 +104,7 @@ public class WorkgroupProperties extends IQ {
while (!done) { while (!done) {
int eventType = parser.next(); int eventType = parser.next();
if ((eventType == XmlPullParser.START_TAG) && ("authRequired".equals(parser.getName()))) { if ((eventType == XmlPullParser.START_TAG) && ("authRequired".equals(parser.getName()))) {
// CHECKSTYLE:OFF
props.setAuthRequired(Boolean.valueOf(parser.nextText()).booleanValue()); props.setAuthRequired(Boolean.valueOf(parser.nextText()).booleanValue());
// CHECKSTYLE:ON
} }
else if ((eventType == XmlPullParser.START_TAG) && ("email".equals(parser.getName()))) { else if ((eventType == XmlPullParser.START_TAG) && ("email".equals(parser.getName()))) {
props.setEmail(parser.nextText()); props.setEmail(parser.nextText());

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2013-2019 Florian Schmaus * Copyright 2013-2018 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -41,7 +41,6 @@ import org.minidns.dnsname.DnsName;
/** /**
* A DNS resolver (mostly for SRV records), which makes use of the API provided in the javax.* namespace. * A DNS resolver (mostly for SRV records), which makes use of the API provided in the javax.* namespace.
* Note that using JavaxResovler requires applications using newer Java versions (at least 11) to declare a dependency on the "sun.jdk" module.
* *
* @author Florian Schmaus * @author Florian Schmaus
* *
@ -56,8 +55,8 @@ public class JavaxResolver extends DNSResolver implements SmackInitializer {
Hashtable<String, String> env = new Hashtable<>(); Hashtable<String, String> env = new Hashtable<>();
env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
dirContext = new InitialDirContext(env); dirContext = new InitialDirContext(env);
} catch (NamingException e) { } catch (Exception e) {
LOGGER.log(Level.SEVERE, "Could not construct InitialDirContext", e); // Ignore.
} }
// Try to set this DNS resolver as primary one // Try to set this DNS resolver as primary one

View file

@ -16,13 +16,10 @@
*/ */
package org.jivesoftware.smack.sm; package org.jivesoftware.smack.sm;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.BlockingQueue;
import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.packet.Element;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
public abstract class StreamManagementException extends SmackException { public abstract class StreamManagementException extends SmackException {
@ -113,56 +110,5 @@ public abstract class StreamManagementException extends SmackException {
return ackedStanzas; return ackedStanzas;
} }
} }
public static final class UnacknowledgedQueueFullException extends StreamManagementException {
/**
*
*/
private static final long serialVersionUID = 1L;
private final int overflowElementNum;
private final int droppedElements;
private final List<Element> elements;
private final List<Stanza> unacknowledgesStanzas;
private UnacknowledgedQueueFullException(String message, int overflowElementNum, int droppedElements, List<Element> elements,
List<Stanza> unacknowledgesStanzas) {
super(message);
this.overflowElementNum = overflowElementNum;
this.droppedElements = droppedElements;
this.elements = elements;
this.unacknowledgesStanzas = unacknowledgesStanzas;
}
public int getOverflowElementNum() {
return overflowElementNum;
}
public int getDroppedElements() {
return droppedElements;
}
public List<Element> getElements() {
return elements;
}
public List<Stanza> getUnacknowledgesStanzas() {
return unacknowledgesStanzas;
}
public static UnacknowledgedQueueFullException newWith(int overflowElementNum, List<Element> elements,
BlockingQueue<Stanza> unacknowledgedStanzas) {
final int unacknowledgesStanzasQueueSize = unacknowledgedStanzas.size();
List<Stanza> localUnacknowledgesStanzas = new ArrayList<>(unacknowledgesStanzasQueueSize);
localUnacknowledgesStanzas.addAll(unacknowledgedStanzas);
int droppedElements = elements.size() - overflowElementNum - 1;
String message = "The queue size " + unacknowledgesStanzasQueueSize + " is not able to fit another "
+ droppedElements + " potential stanzas type top-level stream-elements.";
return new UnacknowledgedQueueFullException(message, overflowElementNum, droppedElements, elements,
localUnacknowledgesStanzas);
}
}
} }

View file

@ -51,7 +51,6 @@ import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
@ -84,8 +83,6 @@ import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.SmackException.NotLoggedInException; import org.jivesoftware.smack.SmackException.NotLoggedInException;
import org.jivesoftware.smack.SmackException.SecurityRequiredByServerException; import org.jivesoftware.smack.SmackException.SecurityRequiredByServerException;
import org.jivesoftware.smack.SmackException.SmackWrappedException;
import org.jivesoftware.smack.SmackFuture;
import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.SynchronizationPoint; import org.jivesoftware.smack.SynchronizationPoint;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
@ -168,17 +165,15 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
private SSLSocket secureSocket; private SSLSocket secureSocket;
private final Semaphore readerWriterSemaphore = new Semaphore(2); /**
* Protected access level because of unit test purposes
*/
protected PacketWriter packetWriter;
/** /**
* Protected access level because of unit test purposes * Protected access level because of unit test purposes
*/ */
protected final PacketWriter packetWriter = new PacketWriter(); protected PacketReader packetReader;
/**
* Protected access level because of unit test purposes
*/
protected final PacketReader packetReader = new PacketReader();
private final SynchronizationPoint<Exception> initialOpenStreamSend = new SynchronizationPoint<>( private final SynchronizationPoint<Exception> initialOpenStreamSend = new SynchronizationPoint<>(
this, "initial open stream element send to server"); this, "initial open stream element send to server");
@ -283,15 +278,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
*/ */
private final Collection<StanzaListener> stanzaAcknowledgedListeners = new ConcurrentLinkedQueue<>(); private final Collection<StanzaListener> stanzaAcknowledgedListeners = new ConcurrentLinkedQueue<>();
/**
* These listeners are invoked for every stanza that got dropped.
* <p>
* We use a {@link ConcurrentLinkedQueue} here in order to allow the listeners to remove
* themselves after they have been invoked.
* </p>
*/
private final Collection<StanzaListener> stanzaDroppedListeners = new ConcurrentLinkedQueue<>();
/** /**
* This listeners are invoked for a acknowledged stanza that has the given stanza ID. They will * This listeners are invoked for a acknowledged stanza that has the given stanza ID. They will
* only be invoked once and automatically removed after that. * only be invoked once and automatically removed after that.
@ -401,11 +387,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
SSLSession sslSession = secureSocket != null ? secureSocket.getSession() : null; SSLSession sslSession = secureSocket != null ? secureSocket.getSession() : null;
saslAuthentication.authenticate(username, password, config.getAuthzid(), sslSession); saslAuthentication.authenticate(username, password, config.getAuthzid(), sslSession);
// Wait for stream features after the authentication.
// TODO: The name of this synchronization point "maybeCompressFeaturesReceived" is not perfect. It should be
// renamed to "streamFeaturesAfterAuthenticationReceived".
maybeCompressFeaturesReceived.checkIfSuccessOrWait();
// If compression is enabled then request the server to use stream compression. XEP-170 // If compression is enabled then request the server to use stream compression. XEP-170
// recommends to perform stream compression before resource binding. // recommends to perform stream compression before resource binding.
maybeEnableCompression(); maybeEnableCompression();
@ -456,25 +437,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
} }
} }
} }
// Inform client about failed resumption if possible, resend stanzas otherwise // (Re-)send the stanzas *after* we tried to enable SM
// Process the stanzas synchronously so a client can re-queue them for transmission
// before it is informed about connection success
if (!stanzaDroppedListeners.isEmpty()) {
for (Stanza stanza : previouslyUnackedStanzas) {
for (StanzaListener listener : stanzaDroppedListeners) {
try {
listener.processStanza(stanza);
}
catch (InterruptedException | NotConnectedException | NotLoggedInException e) {
LOGGER.log(Level.FINER, "StanzaDroppedListener received exception", e);
}
}
}
} else {
for (Stanza stanza : previouslyUnackedStanzas) { for (Stanza stanza : previouslyUnackedStanzas) {
sendStanzaInternal(stanza); sendStanzaInternal(stanza);
} }
}
afterSuccessfulLogin(false); afterSuccessfulLogin(false);
} }
@ -502,16 +468,24 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
shutdown(false); shutdown(false);
} }
@Override /**
* Performs an unclean disconnect and shutdown of the connection. Does not send a closing stream stanza.
*/
public synchronized void instantShutdown() { public synchronized void instantShutdown() {
shutdown(true); shutdown(true);
} }
private void shutdown(boolean instant) { private void shutdown(boolean instant) {
if (disconnectedButResumeable) {
return;
}
// First shutdown the writer, this will result in a closing stream element getting send to // First shutdown the writer, this will result in a closing stream element getting send to
// the server // the server
if (packetWriter != null) {
LOGGER.finer("PacketWriter shutdown()"); LOGGER.finer("PacketWriter shutdown()");
packetWriter.shutdown(instant); packetWriter.shutdown(instant);
}
LOGGER.finer("PacketWriter has been shut down"); LOGGER.finer("PacketWriter has been shut down");
if (!instant) { if (!instant) {
@ -526,29 +500,19 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
} }
} }
if (packetReader != null) {
LOGGER.finer("PacketReader shutdown()"); LOGGER.finer("PacketReader shutdown()");
packetReader.shutdown(); packetReader.shutdown();
}
LOGGER.finer("PacketReader has been shut down"); LOGGER.finer("PacketReader has been shut down");
final Socket socket = this.socket;
if (socket != null && socket.isConnected()) {
try { try {
socket.close(); socket.close();
} catch (Exception e) { } catch (Exception e) {
LOGGER.log(Level.WARNING, "shutdown", e); LOGGER.log(Level.WARNING, "shutdown", e);
} }
}
setWasAuthenticated(); setWasAuthenticated();
// Wait for reader and writer threads to be terminated.
readerWriterSemaphore.acquireUninterruptibly(2);
readerWriterSemaphore.release(2);
if (disconnectedButResumeable) {
return;
}
// If we are able to resume the stream, then don't set // If we are able to resume the stream, then don't set
// connected/authenticated/usingTLS to false since we like behave like we are still // connected/authenticated/usingTLS to false since we like behave like we are still
// connected (e.g. sendStanza should not throw a NotConnectedException). // connected (e.g. sendStanza should not throw a NotConnectedException).
@ -559,8 +523,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
// Reset the stream management session id to null, since if the stream is cleanly closed, i.e. sending a closing // Reset the stream management session id to null, since if the stream is cleanly closed, i.e. sending a closing
// stream tag, there is no longer a stream to resume. // stream tag, there is no longer a stream to resume.
smSessionId = null; smSessionId = null;
// Note that we deliberately do not reset authenticatedConnectionInitiallyEstablishedTimestamp here, so that the
// information is available in the connectionClosedOnError() listeners.
} }
authenticated = false; authenticated = false;
connected = false; connected = false;
@ -568,12 +530,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
reader = null; reader = null;
writer = null; writer = null;
initState();
}
@Override
protected void initState() {
super.initState();
maybeCompressFeaturesReceived.init(); maybeCompressFeaturesReceived.init();
compressSyncPoint.init(); compressSyncPoint.init();
smResumedSyncPoint.init(); smResumedSyncPoint.init();
@ -599,7 +555,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
} }
} }
private void connectUsingConfiguration() throws ConnectionException, IOException, InterruptedException { private void connectUsingConfiguration() throws ConnectionException, IOException {
List<HostAddress> failedAddresses = populateHostAddresses(); List<HostAddress> failedAddresses = populateHostAddresses();
SocketFactory socketFactory = config.getSocketFactory(); SocketFactory socketFactory = config.getSocketFactory();
ProxyInfo proxyInfo = config.getProxyInfo(); ProxyInfo proxyInfo = config.getProxyInfo();
@ -618,16 +574,14 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
innerloop: while (inetAddresses.hasNext()) { innerloop: while (inetAddresses.hasNext()) {
// Create a *new* Socket before every connection attempt, i.e. connect() call, since Sockets are not // Create a *new* Socket before every connection attempt, i.e. connect() call, since Sockets are not
// re-usable after a failed connection attempt. See also SMACK-724. // re-usable after a failed connection attempt. See also SMACK-724.
SmackFuture.SocketFuture socketFuture = new SmackFuture.SocketFuture(socketFactory); socket = socketFactory.createSocket();
final InetAddress inetAddress = inetAddresses.next(); final InetAddress inetAddress = inetAddresses.next();
final InetSocketAddress inetSocketAddress = new InetSocketAddress(inetAddress, port); final String inetAddressAndPort = inetAddress + " at port " + port;
LOGGER.finer("Trying to establish TCP connection to " + inetSocketAddress); LOGGER.finer("Trying to establish TCP connection to " + inetAddressAndPort);
socketFuture.connectAsync(inetSocketAddress, timeout);
try { try {
socket = socketFuture.getOrThrow(); socket.connect(new InetSocketAddress(inetAddress, port), timeout);
} catch (IOException e) { } catch (Exception e) {
hostAddress.setException(inetAddress, e); hostAddress.setException(inetAddress, e);
if (inetAddresses.hasNext()) { if (inetAddresses.hasNext()) {
continue innerloop; continue innerloop;
@ -635,7 +589,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
break innerloop; break innerloop;
} }
} }
LOGGER.finer("Established TCP connection to " + inetSocketAddress); LOGGER.finer("Established TCP connection to " + inetAddressAndPort);
// We found a host to connect to, return here // We found a host to connect to, return here
this.host = host; this.host = host;
this.port = port; this.port = port;
@ -651,7 +605,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
proxyInfo.getProxySocketConnection().connect(socket, host, port, timeout); proxyInfo.getProxySocketConnection().connect(socket, host, port, timeout);
} catch (IOException e) { } catch (IOException e) {
hostAddress.setException(e); hostAddress.setException(e);
failedAddresses.add(hostAddress);
continue; continue;
} }
LOGGER.finer("Established TCP connection to " + hostAndPort); LOGGER.finer("Established TCP connection to " + hostAndPort);
@ -674,23 +627,18 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
* @throws XMPPException if establishing a connection to the server fails. * @throws XMPPException if establishing a connection to the server fails.
* @throws SmackException if the server fails to respond back or if there is anther error. * @throws SmackException if the server fails to respond back or if there is anther error.
* @throws IOException * @throws IOException
* @throws InterruptedException
*/ */
private void initConnection() throws IOException, InterruptedException { private void initConnection() throws IOException {
boolean isFirstInitialization = packetReader == null || packetWriter == null;
compressionHandler = null; compressionHandler = null;
// Set the reader and writer instance variables // Set the reader and writer instance variables
initReaderAndWriter(); initReaderAndWriter();
int availableReaderWriterSemaphorePermits = readerWriterSemaphore.availablePermits(); if (isFirstInitialization) {
if (availableReaderWriterSemaphorePermits < 2) { packetWriter = new PacketWriter();
Object[] logObjects = new Object[] { packetReader = new PacketReader();
this,
availableReaderWriterSemaphorePermits,
};
LOGGER.log(Level.FINE, "Not every reader/writer threads where terminated on connection re-initializtion of {0}. Available permits {1}", logObjects);
} }
readerWriterSemaphore.acquire(2);
// Start the writer thread. This will open an XMPP stream to the server // Start the writer thread. This will open an XMPP stream to the server
packetWriter.init(); packetWriter.init();
// Start the reader thread. The startup() method will block until we // Start the reader thread. The startup() method will block until we
@ -911,7 +859,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
if (!config.isCompressionEnabled()) { if (!config.isCompressionEnabled()) {
return; return;
} }
maybeCompressFeaturesReceived.checkIfSuccessOrWait();
Compress.Feature compression = getFeature(Compress.Feature.ELEMENT, Compress.NAMESPACE); Compress.Feature compression = getFeature(Compress.Feature.ELEMENT, Compress.NAMESPACE);
if (compression == null) { if (compression == null) {
// Server does not support compression // Server does not support compression
@ -962,43 +910,19 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
* *
* @param e the exception that causes the connection close event. * @param e the exception that causes the connection close event.
*/ */
private void notifyConnectionError(final Exception e) { private synchronized void notifyConnectionError(Exception e) {
ASYNC_BUT_ORDERED.performAsyncButOrdered(this, new Runnable() {
@Override
public void run() {
// Listeners were already notified of the exception, return right here. // Listeners were already notified of the exception, return right here.
if (packetReader.done || packetWriter.done()) return; if ((packetReader == null || packetReader.done) &&
(packetWriter == null || packetWriter.done())) return;
// Report the failure outside the synchronized block, so that a thread waiting within a synchronized
// function like connect() throws the wrapped exception.
SmackWrappedException smackWrappedException = new SmackWrappedException(e);
tlsHandled.reportGenericFailure(smackWrappedException);
saslFeatureReceived.reportGenericFailure(smackWrappedException);
maybeCompressFeaturesReceived.reportGenericFailure(smackWrappedException);
lastFeaturesReceived.reportGenericFailure(smackWrappedException);
synchronized (XMPPTCPConnection.this) {
// Within this synchronized block, either *both* reader and writer threads must be terminated, or
// none.
assert ((packetReader.done && packetWriter.done())
|| (!packetReader.done && !packetWriter.done()));
// Closes the connection temporary. A reconnection is possible // Closes the connection temporary. A reconnection is possible
// Note that a connection listener of XMPPTCPConnection will drop the SM state in // Note that a connection listener of XMPPTCPConnection will drop the SM state in
// case the Exception is a StreamErrorException. // case the Exception is a StreamErrorException.
instantShutdown(); instantShutdown();
}
Async.go(new Runnable() {
@Override
public void run() {
// Notify connection listeners of the error. // Notify connection listeners of the error.
callConnectionClosedOnErrorListener(e); callConnectionClosedOnErrorListener(e);
} }
}, XMPPTCPConnection.this + " callConnectionClosedOnErrorListener()");
}
});
}
/** /**
* For unit testing purposes * For unit testing purposes
@ -1010,13 +934,14 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
} }
@Override @Override
protected void afterFeaturesReceived() throws NotConnectedException, InterruptedException, SecurityRequiredByServerException { protected void afterFeaturesReceived() throws NotConnectedException, InterruptedException {
StartTls startTlsFeature = getFeature(StartTls.ELEMENT, StartTls.NAMESPACE); StartTls startTlsFeature = getFeature(StartTls.ELEMENT, StartTls.NAMESPACE);
if (startTlsFeature != null) { if (startTlsFeature != null) {
if (startTlsFeature.required() && config.getSecurityMode() == SecurityMode.disabled) { if (startTlsFeature.required() && config.getSecurityMode() == SecurityMode.disabled) {
SecurityRequiredByServerException smackException = new SecurityRequiredByServerException(); SmackException smackException = new SecurityRequiredByServerException();
tlsHandled.reportFailure(smackException); tlsHandled.reportFailure(smackException);
throw smackException; notifyConnectionError(smackException);
return;
} }
if (config.getSecurityMode() != ConnectionConfiguration.SecurityMode.disabled) { if (config.getSecurityMode() != ConnectionConfiguration.SecurityMode.disabled) {
@ -1067,8 +992,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
protected class PacketReader { protected class PacketReader {
private final String threadName = "Smack Reader (" + getConnectionCounter() + ')';
XmlPullParser parser; XmlPullParser parser;
private volatile boolean done; private volatile boolean done;
@ -1083,15 +1006,9 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
Async.go(new Runnable() { Async.go(new Runnable() {
@Override @Override
public void run() { public void run() {
LOGGER.finer(threadName + " start");
try {
parsePackets(); parsePackets();
} finally {
LOGGER.finer(threadName + " exit");
XMPPTCPConnection.this.readerWriterSemaphore.release();
} }
} }, "Smack Reader (" + getConnectionCounter() + ")");
}, threadName);
} }
/** /**
@ -1311,11 +1228,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
LOGGER.info(XMPPTCPConnection.this LOGGER.info(XMPPTCPConnection.this
+ " received closing </stream> element." + " received closing </stream> element."
+ " Server wants to terminate the connection, calling disconnect()"); + " Server wants to terminate the connection, calling disconnect()");
ASYNC_BUT_ORDERED.performAsyncButOrdered(XMPPTCPConnection.this, new Runnable() {
@Override
public void run() {
disconnect(); disconnect();
}});
} }
} }
break; break;
@ -1344,8 +1257,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
protected class PacketWriter { protected class PacketWriter {
public static final int QUEUE_SIZE = XMPPTCPConnection.QUEUE_SIZE; public static final int QUEUE_SIZE = XMPPTCPConnection.QUEUE_SIZE;
private final String threadName = "Smack Writer (" + getConnectionCounter() + ')';
private final ArrayBlockingQueueWithShutdown<Element> queue = new ArrayBlockingQueueWithShutdown<>( private final ArrayBlockingQueueWithShutdown<Element> queue = new ArrayBlockingQueueWithShutdown<>(
QUEUE_SIZE, true); QUEUE_SIZE, true);
@ -1391,15 +1302,9 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
Async.go(new Runnable() { Async.go(new Runnable() {
@Override @Override
public void run() { public void run() {
LOGGER.finer(threadName + " start");
try {
writePackets(); writePackets();
} finally {
LOGGER.finer(threadName + " exit");
XMPPTCPConnection.this.readerWriterSemaphore.release();
} }
} }, "Smack Writer (" + getConnectionCounter() + ")");
}, threadName);
} }
private boolean done() { private boolean done() {
@ -1450,12 +1355,11 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
instantShutdown = instant; instantShutdown = instant;
queue.shutdown(); queue.shutdown();
shutdownTimestamp = System.currentTimeMillis(); shutdownTimestamp = System.currentTimeMillis();
if (shutdownDone.isNotInInitialState()) {
try { try {
shutdownDone.checkIfSuccessOrWait(); shutdownDone.checkIfSuccessOrWait();
} catch (NoResponseException | InterruptedException e) {
LOGGER.log(Level.WARNING, "shutdownDone was not marked as successful by the writer thread", e);
} }
catch (NoResponseException | InterruptedException e) {
LOGGER.log(Level.WARNING, "shutdownDone was not marked as successful by the writer thread", e);
} }
} }
@ -1610,16 +1514,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
private void drainWriterQueueToUnacknowledgedStanzas() { private void drainWriterQueueToUnacknowledgedStanzas() {
List<Element> elements = new ArrayList<>(queue.size()); List<Element> elements = new ArrayList<>(queue.size());
queue.drainTo(elements); queue.drainTo(elements);
for (int i = 0; i < elements.size(); i++) { for (Element element : elements) {
Element element = elements.get(i);
// If the unacknowledgedStanza queue is full, then bail out with a warning message. See SMACK-844.
if (unacknowledgedStanzas.remainingCapacity() == 0) {
StreamManagementException.UnacknowledgedQueueFullException exception = StreamManagementException.UnacknowledgedQueueFullException
.newWith(i, elements, unacknowledgedStanzas);
LOGGER.log(Level.WARNING,
"Some stanzas may be lost as not all could be drained to the unacknowledged stanzas queue", exception);
return;
}
if (element instanceof Stanza) { if (element instanceof Stanza) {
unacknowledgedStanzas.add((Stanza) element); unacknowledgedStanzas.add((Stanza) element);
} }
@ -1824,32 +1719,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
stanzaAcknowledgedListeners.clear(); stanzaAcknowledgedListeners.clear();
} }
/**
* Add a Stanza dropped listener.
* <p>
* Those listeners will be invoked every time a Stanza has been dropped due to a failed SM resume. They will not get
* automatically removed. If at least one StanzaDroppedListener is configured, no attempt will be made to retransmit
* the Stanzas.
* </p>
*
* @param listener the listener to add.
* @since 4.3.3
*/
public void addStanzaDroppedListener(StanzaListener listener) {
stanzaDroppedListeners.add(listener);
}
/**
* Remove the given Stanza dropped listener.
*
* @param listener the listener.
* @return true if the listener was removed.
* @since 4.3.3
*/
public boolean removeStanzaDroppedListener(StanzaListener listener) {
return stanzaDroppedListeners.remove(listener);
}
/** /**
* Add a new Stanza ID acknowledged listener for the given ID. * Add a new Stanza ID acknowledged listener for the given ID.
* <p> * <p>

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2014-2019 Florian Schmaus * Copyright 2014 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -49,9 +49,11 @@ public class PacketWriterTest {
@Test @Test
public void shouldBlockAndUnblockTest() throws InterruptedException, BrokenBarrierException, NotConnectedException, XmppStringprepException { public void shouldBlockAndUnblockTest() throws InterruptedException, BrokenBarrierException, NotConnectedException, XmppStringprepException {
XMPPTCPConnection connection = new XMPPTCPConnection("user", "pass", "example.org"); XMPPTCPConnection connection = new XMPPTCPConnection("user", "pass", "example.org");
final PacketWriter pw = connection.packetWriter; final PacketWriter pw = connection.new PacketWriter();
connection.packetWriter = pw;
connection.packetReader = connection.new PacketReader();
connection.setWriter(new BlockingStringWriter()); connection.setWriter(new BlockingStringWriter());
connection.packetWriter.init(); pw.init();
for (int i = 0; i < XMPPTCPConnection.PacketWriter.QUEUE_SIZE; i++) { for (int i = 0; i < XMPPTCPConnection.PacketWriter.QUEUE_SIZE; i++) {
pw.sendStreamElement(new Message()); pw.sendStreamElement(new Message());

View file

@ -1,15 +1,9 @@
allprojects { allprojects {
ext { ext {
shortVersion = '4.3.4' shortVersion = '4.3.1'
isSnapshot = true isSnapshot = true
// When using dynamic versions for those, do *not* use [1.0, jxmppVersion = '0.6.3'
// 2.0), since this will also pull in 2.0-alpha1. Instead use miniDnsVersion = '0.3.2'
// [1.0, 1.0.99].
// See also:
// - https://issues.apache.org/jira/browse/MNG-6232
// - https://issues.igniterealtime.org/browse/SMACK-858
jxmppVersion = '[0.6, 0.6.999]'
miniDnsVersion = '[0.3, 0.3.999]'
smackMinAndroidSdk = 9 smackMinAndroidSdk = 9
} }
} }