and also call notifyConnectionError() on exception thrown by
openStream().
In hindsight I wonder why openStream() was ever called in the writer
thread, as it only caused unnecessary synchronization overhead, as can
be seen by the initialOpenStreamSend synchronization point.
In case an exception happens in connect()/login() the
'disconnectedButResumable' boolean may (still) be set. Which causes
only one of the reader and writer threads to exit, typically the
reader thread, because shutdown() will bail out very early. This
leaves a dangling (writer) thread causing memory leaks and deadlocks
on a subsequent connect()/login().
as it is not neccessary and causes stalls and deadlocks with a
concurrent connect()/login() (which could be resolved interrupting the
connect()/login() calling thread):
"Smack Reader (0)" daemon prio=5 tid=21 Blocked
| group="main" sCount=1 dsCount=0 obj=0x12c84670 self=0x7e072ca200
| sysTid=14965 nice=0 cgrp=default sched=0/0 handle=0x7e06155450
| state=S schedstat=( 63430626 3034269 21 ) utm=5 stm=0 core=2 HZ=100
| stack=0x7e06053000-0x7e06055000 stackSize=1037KB
| held mutexes=
at org.jivesoftware.smack.tcp.XMPPTCPConnection.notifyConnectionError(XMPPTCPConnection.java:-1)
- waiting to lock <0x0ec6e6bb> (a org.jivesoftware.smack.tcp.XMPPTCPConnection) held by thread 22
at org.jivesoftware.smack.tcp.XMPPTCPConnection.access$3900(XMPPTCPConnection.java:154)
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets(XMPPTCPConnection.java:1330)
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$900(XMPPTCPConnection.java:1064)
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run(XMPPTCPConnection.java:1081)
at java.lang.Thread.run(Thread.java:761)
"connect" prio=5 tid=22 Waiting
| group="main" sCount=1 dsCount=0 obj=0x12c8e820 self=0x7e19bcd600
| sysTid=15047 nice=0 cgrp=default sched=0/0 handle=0x7e05ee4450
| state=S schedstat=( 14067083 7475835 14 ) utm=1 stm=0 core=0 HZ=100
| stack=0x7e05de2000-0x7e05de4000 stackSize=1037KB
| held mutexes=
at java.lang.Object.wait!(Native method)
- waiting on <0x0c058b14> (a java.lang.Object)
at java.lang.Thread.parkFor$(Thread.java:2127)
- locked <0x0c058b14> (a java.lang.Object)
at sun.misc.Unsafe.park(Unsafe.java:325)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:161)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:840)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:994)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1303)
at java.util.concurrent.Semaphore.acquire(Semaphore.java:446)
at org.jivesoftware.smack.tcp.XMPPTCPConnection.initConnection(XMPPTCPConnection.java:685)
at org.jivesoftware.smack.tcp.XMPPTCPConnection.connectInternal(XMPPTCPConnection.java:942)
at org.jivesoftware.smack.AbstractXMPPConnection.connect(AbstractXMPPConnection.java:417)
- locked <0x0ec6e6bb> (a org.jivesoftware.smack.tcp.XMPPTCPConnection)
at org.yaxim.androidclient.service.SmackableImp.connectAndLogin(SmackableImp.java:717)
- locked <0x0ec6e6bb> (a org.jivesoftware.smack.tcp.XMPPTCPConnection)
at org.yaxim.androidclient.service.SmackableImp.doConnect(SmackableImp.java:304)
at org.yaxim.androidclient.service.SmackableImp$5.run(SmackableImp.java:391)
If a stream resume fails, smack will re-send all queued stanzas after a
reconnect. However, it does not make sense to re-send them:
* IQs / IQ responses have probably timed out
* MUC messages and PMs will be rejected as you haven't rejoined yet
* regular messages should be amended with a <delay> element
This patch adds a StanzaDroppedListener interface to the
XMPPTCPConnection. If at least one StanzaDroppedListener is provided,
all queued messages will be drained into the StanzaDroppedListener(s).
Otherwise, the original behavior of attempting to transmit them will be
followed.
Discussion: https://discourse.igniterealtime.org/t/xep-0198-resume-failure-reconnect-resending-of-muc-messages/83510/3
Signed-off-by: Georg Lukas <georg@op-co.de>
to clean up the state build up by connect().
Related to SMACK-855 there is the possiblitiy of a stray (writer)
thread if, for example, tlsHandled.checkifSuccessOrWaitorThrow() in
XMPPTCPConnection.connectInternal() throws. This commit should prevent
that.
This commit adds
- SmackReactor / NIO
- a framework for finite state machine connections
- support for Java 8
- pretty printed XML debug output
It also
- reworks the integration test framework
- raises the minimum Android API level to 19
- introduces XmppNioTcpConnection
Furthermore fixes SMACK-801 (at least partly). Java 8 language
features are available, but not all runtime library methods. For that
we would need to raise the Android API level to 24 or higher.
The inetAddressAndPort String is redundant since
a2743549b8, because we now construct the
InetSocketAddress earlier and can hence use it in the log statement.
The purpose of the "maybeCompressFeaturesReceived" synchronization
point is to wait for the stream features after authentication. This is
not really reflected by its name and furthermore its
checkIfSuccessOrWait() method was only invoked if compression was
enabled, but we always want to wait for the stream features after
authentication. Hence move the call before the isCompressionEnabled()
check and one layer up in the call stack.
Fixes SMACK-846.
using the default algorithm. Instead continue with 'null' as value of
the KeyManager[] array (kms). This makes the SSLContext.init() methods
to search the default security providers for implementations, which is
also OK.
This change is needed because it appears that on Android
KeyManagerFactory.getDefaultAlgorithm returns 'SunX509', which
subsequently results in
W/AbstractXMPPConnection: Connection XMPPTCPConnection[not-authenticated] (0) closed with error
java.security.NoSuchAlgorithmException: KeyManagerFactory SunX509 implementation not found
at org.apache.harmony.security.fortress.Engine.notFound(Engine.java:190)
at org.apache.harmony.security.fortress.Engine.getInstance(Engine.java:139)
at javax.net.ssl.KeyManagerFactory.getInstance(KeyManagerFactory.java:77)
at org.jivesoftware.smack.tcp.XMPPTCPConnection.proceedTLSReceived(XMPPTCPConnection.java:747)
at org.jivesoftware.smack.tcp.XMPPTCPConnection.access$1200(XMPPTCPConnection.java:149)
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets(XMPPTCPConnection.java:1053)
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$300(XMPPTCPConnection.java:980)
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run(XMPPTCPConnection.java:996)
at java.lang.Thread.run(Thread.java:818)
Note that this is possibly because the Secuurity Provider was
not (yet) intialized.
New stanzas sent directly after stream resumption might have been added
to unacknowledgedStanzas before the old unacknowledged stanzas
are resent. This caused new stanzas to be sent twice and later led
to a StreamManagementCounterError.
Fixes SMACK-786
by calling String.equals() on the constant string and not on the
return value of parser.getName().
Also perform the access to 'parser' on a different LOC than
equals(). This should help debugging things like
okt 25, 2017 2:02:54 PM org.jivesoftware.smack.AbstractXMPPConnection callConnectionClosedOnErrorListener
WARNING: Connection XMPPTCPConnection[***@***/***] (0) closed with error
java.lang.NullPointerException
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets(XMPPTCPConnection.java:1194)
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$300(XMPPTCPConnection.java:982)
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run(XMPPTCPConnection.java:998)
at java.lang.Thread.run(Thread.java:745)
which where recently reported on the forums.
This caused the WaitForClosingStreamElementTest integration test to
fail, because the last presences stanzas, which are send after done()
in the writer thread would return true, are not added to the
unacknowledgedStanzas queue.
The result was:
SEVERE: WaitForClosingStreamElementTest.waitForClosingStreamElementTest (LowLevel): Failed
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.igniterealtime.smack.inttest.SmackIntegrationTestFramework.invokeLowLevel(SmackIntegrationTestFramework.java:466)
at org.igniterealtime.smack.inttest.SmackIntegrationTestFramework.runTests(SmackIntegrationTestFramework.java:375)
at org.igniterealtime.smack.inttest.SmackIntegrationTestFramework.run(SmackIntegrationTestFramework.java:165)
at org.igniterealtime.smack.inttest.SmackIntegrationTestFramework.main(SmackIntegrationTestFramework.java:84)
Caused by: java.lang.AssertionError: Sync poing yielded failure exception
at org.jivesoftware.smack.WaitForClosingStreamElementTest.waitForClosingStreamElementTest(WaitForClosingStreamElementTest.java:46)
... 8 more
Caused by: org.jivesoftware.smack.sm.StreamManagementException$StreamManagementCounterError: There was an error regarding the Stream Mangement counters. Server reported 3 handled stanzas, which means that the 3 recently send stanzas by client are now acked by the server. But Smack had only 1 to acknowledge. The stanza id of the last acked outstanding stanza is FqL1X-144
at org.jivesoftware.smack.tcp.XMPPTCPConnection.processHandledCount(XMPPTCPConnection.java:1847)
at org.jivesoftware.smack.tcp.XMPPTCPConnection.access$2600(XMPPTCPConnection.java:149)
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets(XMPPTCPConnection.java:1176)
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$300(XMPPTCPConnection.java:980)
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run(XMPPTCPConnection.java:996)
at java.lang.Thread.run(Thread.java:745)
Before this, if there was a stream error response by the server to our
stream open, that error response would only be handled in the reader
thread, and the user would get a message like:
"org.jivesoftware.smack.SmackException$NoResponseException: No
response received within reply timeout. Timeout was
5000ms (~5s). While waiting for SASL mechanisms stream feature from
server"
while the server may actually sent something like
<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
id='6785787028201586334'
from='jabbim.com'
version='1.0'
xml:lang='en'>
<stream:error>
<policy-violation xmlns='urn:ietf:params:xml:ns:xmpp-streams'>
</policy-violation>
<text xml:lang='en' xmlns='urn:ietf:params:xml:ns:xmpp-streams'>
Too many (2) failed authentications from this IP
address (1xx.66.xx.xxx). The address will be unblocked at 04:24:00
06.01.2017 UTC
</text>
</stream:error>
</stream:stream>
It was necessary to change saslFeatureReceived from SmackException to
XMPPException in order to return the StreamErrorException at this sync
point. But this change in return required the introduction of a
tlsHandled sync point for SmackException (which just acts as a wrapper
for the various exception types that could occurn when establishing
TLS). The tlsHandled sync point is marked successful even if no TLS
was established in case none was required and/or if not supported by
the server.
Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: -1
at java.lang.String.charAt(String.java:658)
at org.jivesoftware.smack.util.dns.HostAddress.<init>(HostAddress.java:48)
at org.jivesoftware.smack.util.dns.HostAddress.<init>(HostAddress.java:62)