Smack 4.2.0-rc2

-----BEGIN PGP SIGNATURE-----
 
 iQF8BAABCgBmBQJYe7kjXxSAAAAAAC4AKGlzc3Vlci1mcHJAbm90YXRpb25zLm9w
 ZW5wZ3AuZmlmdGhob3JzZW1hbi5uZXQ5Nzc1MDU5RjNBMjFEQ0UxNkJFNEZCQUUy
 MjM5QTdFOEY1ODUyMDUyAAoJECI5p+j1hSBSuGkH/1mU66MNR0/ywbdZ2RKcb9MK
 WTHBBOuD/KaqkQOQQ5Fud+ktyahmDd9Nk4TbchIJ56PlEPqJbhnwP8txh1gU+8Zz
 jpRHYYQiTEuLSblToVL3afrfd/IQnOrh7VbwkG7S1wkWmOFKCv94wlv/OgZFaICc
 RHHJyJbJ0TTuavEkJ141ruNYmq/qYjUjdLmFrqhT3hv3iuiHCT1PK1787wEB0EWS
 ciwkw2j/j/2i27XNQRwu7QaLsmUGk8rLnr2/AsYpmsBsjAMqMbNEQEPbIgO9MVef
 7dt/GWoqHjH1opVcOw+rRz/cynMii2sSTXiqRVTEZsWlx3uYYz5eKsuiuckZcBs=
 =yqh1
 -----END PGP SIGNATURE-----

Merge tag '4.2.0-rc2'

Smack 4.2.0-rc2
This commit is contained in:
Florian Schmaus 2017-01-15 19:03:32 +01:00
commit 382ed9d871
53 changed files with 797 additions and 182 deletions

View File

@ -69,6 +69,7 @@ Smack Extensions and currently supported XEPs of smack-extensions
| XMPP Over BOSH | [XEP-0206](http://xmpp.org/extensions/xep-0206.html) | Use Bidirectional-streams Over Synchronous HTTP (BOSH) to transport XMPP stanzas. | | XMPP Over BOSH | [XEP-0206](http://xmpp.org/extensions/xep-0206.html) | Use Bidirectional-streams Over Synchronous HTTP (BOSH) to transport XMPP stanzas. |
| Attention | [XEP-0224](http://xmpp.org/extensions/xep-0224.html) | Getting attention of another user. | | Attention | [XEP-0224](http://xmpp.org/extensions/xep-0224.html) | Getting attention of another user. |
| Bits of Binary | [XEP-0231](http://xmpp.org/extensions/xep-0231.html) | Including or referring to small bits of binary data in an XML stanza. | | Bits of Binary | [XEP-0231](http://xmpp.org/extensions/xep-0231.html) | Including or referring to small bits of binary data in an XML stanza. |
| Best Practices for Resource Locking | [XEP-0296](https://xmpp.org/extensions/xep-0296.html) | Specifies best practices to be followed by Jabber/XMPP clients about when to lock into, and unlock away from, resources. |
| Last Message Correction | [XEP-0308](http://xmpp.org/extensions/xep-0308.html) | Provides a method for indicating that a message is a correction of the last sent message. | | Last Message Correction | [XEP-0308](http://xmpp.org/extensions/xep-0308.html) | Provides a method for indicating that a message is a correction of the last sent message. |
| [Group Chat Invitations](invitation.md) | n/a | Send invitations to other users to join a group chat room. | | [Group Chat Invitations](invitation.md) | n/a | Send invitations to other users to join a group chat room. |
| [Jive Properties](properties.md) | n/a | TODO | | [Jive Properties](properties.md) | n/a | TODO |

View File

@ -97,8 +97,8 @@ done. The last step is to send the message as you do with any other message.
An XHTML message is like any regular message, therefore to send the message An XHTML message is like any regular message, therefore to send the message
you can follow the usual steps you do in order to send a message. For example, you can follow the usual steps you do in order to send a message. For example,
to send a message as part of a chat just use the message **#send(Message)** of to send a message as part of a chat just use the message **#sendMessage(Message)** of
_**Chat**_ or you can use the message **#send(Stanza)** of _**Chat**_ or you can use the message **#sendStanza(Stanza)** of
_**XMPPConnection**_. _**XMPPConnection**_.
**Example** **Example**
@ -142,19 +142,20 @@ XHTML bodies of any received message.
``` ```
// Create a listener for the chat and display any XHTML content // Create a listener for the chat and display any XHTML content
PacketListener packetListener = new PacketListener() { IncomingChatMessageListener listener = new IncomingChatMessageListener() {
public void processStanza(Stanza stanza) { public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
Message message = (Message) stanza;
// Obtain the XHTML bodies of the message // Obtain the XHTML bodies of the message
List<CharSequence> bodies = XHTMLManager.getBodies(message); List<CharSequence> bodies = XHTMLManager.getBodies(message);
if (bodies != null) { if (bodies == null) {
// Display the bodies on the console return;
for (CharSequence body : bodies) {
System.out.println(body); // Display the bodies on the console
} for (CharSequence body : bodies) {
System.out.println(body);
} }
}
}; };
chat.addMessageListener(packetListener); chatManager.addListener(listener);
``` ```
Discover support for XHTML Messages Discover support for XHTML Messages

View File

@ -70,7 +70,7 @@ created, such as the ability to disable or require encryption. See
Once you've created a connection, you should login with the Once you've created a connection, you should login with the
`XMPPConnection.login()` method. Once you've logged in, you can being `XMPPConnection.login()` method. Once you've logged in, you can being
chatting with other users by creating new `Chat` or `GroupChat` chatting with other users by creating new `Chat` or `MultiUserChat`
objects. objects.
Working with the Roster Working with the Roster
@ -98,18 +98,18 @@ your presence to let people know you're unavailable and "out fishing":
// Create a new presence. Pass in false to indicate we're unavailable._ // Create a new presence. Pass in false to indicate we're unavailable._
Presence presence = new Presence(Presence.Type.unavailable); Presence presence = new Presence(Presence.Type.unavailable);
presence.setStatus("Gone fishing"); presence.setStatus("Gone fishing");
// Send the packet (assume we have an XMPPConnection instance called "con"). // Send the stanza (assume we have an XMPPConnection instance called "con").
con.sendStanza(presence); con.sendStanza(presence);
``` ```
Smack provides two ways to read incoming packets: `PacketListener`, and Smack provides two ways to read incoming packets: `StanzaListener`, and
`PacketCollector`. Both use `StanzaFilter` instances to determine which `StanzaCollector`. Both use `StanzaFilter` instances to determine which
packets should be processed. A packet listener is used for event style stanzas should be processed. A stanza listener is used for event style
programming, while a packet collector has a result queue of packets that you programming, while a stanza collector has a result queue of packets that you
can do polling and blocking operations on. So, a packet listener is useful can do polling and blocking operations on. So, a stanza listener is useful
when you want to take some action whenever a packet happens to come in, while when you want to take some action whenever a stanza happens to come in, while
a packet collector is useful when you want to wait for a specific packet to a stanza collector is useful when you want to wait for a specific packet to
arrive. Packet collectors and listeners can be created using an Connection arrive. Stanza collectors and listeners can be created using an Connection
instance. instance.
Copyright (C) Jive Software 2002-2008 Copyright (C) Jive Software 2002-2008

View File

@ -6,7 +6,7 @@ Messaging using Chats
Sending messages back and forth is at the core of instant messaging. Although Sending messages back and forth is at the core of instant messaging. Although
individual messages can be sent and received as packets, it's generally easier individual messages can be sent and received as packets, it's generally easier
to treat the string of messages as a chat using the to treat the string of messages as a chat using the
`org.jivesoftware.smack.Chat` class. `org.jivesoftware.smack.chat2.Chat` class.
Chat Chat
---- ----
@ -17,80 +17,44 @@ and then send them a text message:
``` ```
// Assume we've created an XMPPConnection name "connection"._ // Assume we've created an XMPPConnection name "connection"._
ChatManager chatmanager = ChatManager.getInstanceFor(connection); ChatManager chatManager = ChatManager.getInstanceFor(connection);
Chat newChat = chatmanager.createChat("jsmith@jivesoftware.com", new MessageListener() { chatManager.addListener(new IncomingChatMessageListener() {
public void processMessage(Chat chat, Message message) { @Override
System.out.println("Received message: " + message); void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
} System.out.println("New message from " + from ": " + message.getBody());
}
}); });
EntityBareJid jid = JidCreate.entityBareFrom("jsmith@jivesoftware.com");
try { Chat chat = chatManager.chatWith(jid);
newChat.sendMessage("Howdy!"); chat.sendMessage("Howdy!");
}
catch (XMPPException e) {
System.out.println("Error Delivering block");
} }
``` ```
The `Chat.sendMessage(String)` method is a convenience method that creates a The `Chat.sendMessage(String)` method is a convenience method that creates a
Message object, sets the body using the String parameter, then sends the Message object, sets the body using the String parameter, then sends the
message. In the case that you wish to set additional values on a Message message. In the case that you wish to set additional values on a Message
before sending it, use the `Chat.createMessage()` and before sending it, use
`Chat.sendMessage(Message)` methods, as in the following code snippet: `Chat.sendMessage(Message)` method, as in the following code snippet:
``` ```
Message newMessage = new Message(); Message newMessage = new Message();
newMessage.setBody("Howdy!"); newMessage.setBody("Howdy!");
// Additional modifications to the message Stanza. // Additional modifications to the message Stanza.
JivePropertiesManager.addProperty(newMessage, "favoriteColor", "red"); JivePropertiesManager.addProperty(newMessage, "favoriteColor", "red");
newChat.sendMessage(newMessage); chat.sendMessage(newMessage);
``` ```
You'll also notice in the example above that we specified a MessageListener You'll also notice in the example above that we specified an IncomingChatMessageListener.
when creating a chat. The listener is notified any time a new message arrives The listener is notified any time a new chat message arrives.
from the other user in the chat. The following code snippet uses the listener The following code snippet uses the listener
as a parrot-bot -- it echoes back everything the other user types. as a parrot-bot -- it echoes back everything the other user types.
``` ```
// Assume a MessageListener we've setup with a chat._ // Assume a IncomingChatMessageListener we've setup with a ChatManager
public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
public void processMessage(Chat chat, Message message) { // Send back the same text the other user sent us.
// Send back the same text the other user sent us._ chat.sendMessage(message.getBody());
chat.sendMessage(message.getBody());
} }
``` ```
Incoming Chat
-------------
When chats are prompted by another user, the setup is slightly different since
you are receiving a chat message first. Instead of explicitly creating a chat
to send messages, you need to register to handle newly created Chat instances
when the ChatManager creates them. The ChatManager will already find a
matching chat (by thread id) and if none exists, then it will create a new one
that does match. To get this new chat, you have to register to be notified
when it happens. You can register a message listener to receive all future
messages as part of this handler.
```
// Assume we've created an XMPPConnection name "connection"._
ChatManager chatManager = ChatManager.getInstanceFor(connection);
chatManager.addChatListener(
new ChatManagerListener() {
@Override
public void chatCreated(Chat chat, boolean createdLocally)
{
if (!createdLocally)
chat.addMessageListener(new MyNewMessageListener());;
}
});
```
In addition to thread based chat messages, there are some clients that do not
send a thread id as part of the chat. To handle this scenario, Smack will
attempt match the incoming messages to the best fit existing chat, based on
the JID. It will attempt to find a chat with the same full JID, failing that,
it will try the base JID. If no existing chat to the user can found, then a
new one is created.
Copyright (C) Jive Software 2002-2008 Copyright (C) Jive Software 2002-2008

View File

@ -182,7 +182,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
// Wait for the response from the server // Wait for the response from the server
synchronized (this) { synchronized (this) {
if (!connected) { if (!connected) {
final long deadline = System.currentTimeMillis() + getPacketReplyTimeout(); final long deadline = System.currentTimeMillis() + getReplyTimeout();
while (!notified) { while (!notified) {
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
if (now >= deadline) break; if (now >= deadline) break;
@ -198,6 +198,9 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
+ getHost() + ":" + getPort() + "."; + getHost() + ":" + getPort() + ".";
throw new SmackException(errorMessage); throw new SmackException(errorMessage);
} }
tlsHandled.reportSuccess();
saslFeatureReceived.reportSuccess();
} }
public boolean isSecureConnection() { public boolean isSecureConnection() {

View File

@ -169,9 +169,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
protected String streamId; protected String streamId;
/** /**
* * The timeout to wait for a reply in milliseconds.
*/ */
private long packetReplyTimeout = SmackConfiguration.getDefaultPacketReplyTimeout(); private long replyTimeout = SmackConfiguration.getDefaultReplyTimeout();
/** /**
* The SmackDebugger allows to log and debug XML traffic. * The SmackDebugger allows to log and debug XML traffic.
@ -188,6 +188,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
*/ */
protected Writer writer; protected Writer writer;
protected final SynchronizationPoint<SmackException> tlsHandled = new SynchronizationPoint<>(this, "establishing TLS");
/** /**
* Set to success if the last features stanza from the server has been parsed. A XMPP connection * Set to success if the last features stanza from the server has been parsed. A XMPP connection
* handshake can invoke multiple features stanzas, e.g. when TLS is activated a second feature * handshake can invoke multiple features stanzas, e.g. when TLS is activated a second feature
@ -198,9 +200,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
AbstractXMPPConnection.this, "last stream features received from server"); AbstractXMPPConnection.this, "last stream features received from server");
/** /**
* Set to success if the sasl feature has been received. * Set to success if the SASL feature has been received.
*/ */
protected final SynchronizationPoint<SmackException> saslFeatureReceived = new SynchronizationPoint<SmackException>( protected final SynchronizationPoint<XMPPException> saslFeatureReceived = new SynchronizationPoint<>(
AbstractXMPPConnection.this, "SASL mechanisms stream feature from server"); AbstractXMPPConnection.this, "SASL mechanisms stream feature from server");
/** /**
@ -368,11 +370,15 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
saslAuthentication.init(); saslAuthentication.init();
saslFeatureReceived.init(); saslFeatureReceived.init();
lastFeaturesReceived.init(); lastFeaturesReceived.init();
tlsHandled.init();
streamId = null; streamId = null;
// Perform the actual connection to the XMPP service // Perform the actual connection to the XMPP service
connectInternal(); connectInternal();
// TLS handled will be successful either if TLS was established, or if it was not mandatory.
tlsHandled.checkIfSuccessOrWaitOrThrow();
// Wait with SASL auth until the SASL mechanisms have been received // Wait with SASL auth until the SASL mechanisms have been received
saslFeatureReceived.checkIfSuccessOrWaitOrThrow(); saslFeatureReceived.checkIfSuccessOrWaitOrThrow();
@ -596,7 +602,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
protected List<HostAddress> hostAddresses; protected List<HostAddress> hostAddresses;
/** /**
* Populates {@link #hostAddresses} with at least one host address. * Populates {@link #hostAddresses} with the resolved addresses or with the configured host address. If no host
* address was configured and all lookups failed, for example with NX_DOMAIN, then {@link #hostAddresses} will be
* populated with the empty list.
* *
* @return a list of host addresses where DNS (SRV) RR resolution failed. * @return a list of host addresses where DNS (SRV) RR resolution failed.
*/ */
@ -610,14 +618,15 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
else if (config.host != null) { else if (config.host != null) {
hostAddresses = new ArrayList<HostAddress>(1); hostAddresses = new ArrayList<HostAddress>(1);
HostAddress hostAddress = DNSUtil.getDNSResolver().lookupHostAddress(config.host, config.port, failedAddresses, config.getDnssecMode()); HostAddress hostAddress = DNSUtil.getDNSResolver().lookupHostAddress(config.host, config.port, failedAddresses, config.getDnssecMode());
hostAddresses.add(hostAddress); if (hostAddress != null) {
hostAddresses.add(hostAddress);
}
} else { } else {
// N.B.: Important to use config.serviceName and not AbstractXMPPConnection.serviceName // N.B.: Important to use config.serviceName and not AbstractXMPPConnection.serviceName
hostAddresses = DNSUtil.resolveXMPPServiceDomain(config.getXMPPServiceDomain().toString(), failedAddresses, config.getDnssecMode()); hostAddresses = DNSUtil.resolveXMPPServiceDomain(config.getXMPPServiceDomain().toString(), failedAddresses, config.getDnssecMode());
} }
// If we reach this, then hostAddresses *must not* be empty, i.e. there is at least one host added, either the // Either the populated host addresses are not empty *or* there must be at least one failed address.
// config.host one or the host representing the service name by DNSUtil assert(!hostAddresses.isEmpty() || !failedAddresses.isEmpty());
assert(!hostAddresses.isEmpty());
return failedAddresses; return failedAddresses;
} }
@ -960,14 +969,26 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
} }
} }
@SuppressWarnings("deprecation")
@Override @Override
public long getPacketReplyTimeout() { public long getPacketReplyTimeout() {
return packetReplyTimeout; return getReplyTimeout();
}
@SuppressWarnings("deprecation")
@Override
public void setPacketReplyTimeout(long timeout) {
setReplyTimeout(timeout);
} }
@Override @Override
public void setPacketReplyTimeout(long timeout) { public long getReplyTimeout() {
packetReplyTimeout = timeout; return replyTimeout;
}
@Override
public void setReplyTimeout(long timeout) {
replyTimeout = timeout;
} }
private static boolean replyToUnknownIqDefault = true; private static boolean replyToUnknownIqDefault = true;
@ -1407,6 +1428,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
// Only proceed with SASL auth if TLS is disabled or if the server doesn't announce it // Only proceed with SASL auth if TLS is disabled or if the server doesn't announce it
if (!hasFeature(StartTls.ELEMENT, StartTls.NAMESPACE) if (!hasFeature(StartTls.ELEMENT, StartTls.NAMESPACE)
|| config.getSecurityMode() == SecurityMode.disabled) { || config.getSecurityMode() == SecurityMode.disabled) {
tlsHandled.reportSuccess();
saslFeatureReceived.reportSuccess(); saslFeatureReceived.reportSuccess();
} }
} }
@ -1456,7 +1478,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
StanzaListener callback, ExceptionCallback exceptionCallback) StanzaListener callback, ExceptionCallback exceptionCallback)
throws NotConnectedException, InterruptedException { throws NotConnectedException, InterruptedException {
sendStanzaWithResponseCallback(stanza, replyFilter, callback, exceptionCallback, sendStanzaWithResponseCallback(stanza, replyFilter, callback, exceptionCallback,
getPacketReplyTimeout()); getReplyTimeout());
} }
@Override @Override
@ -1517,7 +1539,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
@Override @Override
public void sendIqWithResponseCallback(IQ iqRequest, StanzaListener callback, public void sendIqWithResponseCallback(IQ iqRequest, StanzaListener callback,
ExceptionCallback exceptionCallback) throws NotConnectedException, InterruptedException { ExceptionCallback exceptionCallback) throws NotConnectedException, InterruptedException {
sendIqWithResponseCallback(iqRequest, callback, exceptionCallback, getPacketReplyTimeout()); sendIqWithResponseCallback(iqRequest, callback, exceptionCallback, getReplyTimeout());
} }
@Override @Override
@ -1546,7 +1568,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
public void run() { public void run() {
removeSyncStanzaListener(packetListener); removeSyncStanzaListener(packetListener);
} }
}, getPacketReplyTimeout(), TimeUnit.MILLISECONDS); }, getReplyTimeout(), TimeUnit.MILLISECONDS);
} }
@Override @Override

View File

@ -200,7 +200,7 @@ public final class SASLAuthentication {
else { else {
currentMechanism.authenticate(username, host, xmppServiceDomain, password, authzid, sslSession); currentMechanism.authenticate(username, host, xmppServiceDomain, password, authzid, sslSession);
} }
final long deadline = System.currentTimeMillis() + connection.getPacketReplyTimeout(); final long deadline = System.currentTimeMillis() + connection.getReplyTimeout();
while (!authenticationSuccessful && saslException == null) { while (!authenticationSuccessful && saslException == null) {
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
if (now >= deadline) break; if (now >= deadline) break;

View File

@ -101,8 +101,32 @@ public final class SmackConfiguration {
* the server. The default value is 5000 ms. * the server. The default value is 5000 ms.
* *
* @return the milliseconds to wait for a response from the server * @return the milliseconds to wait for a response from the server
* @deprecated use {@link #getDefaultReplyTimeout()} instead.
*/ */
@Deprecated
public static int getDefaultPacketReplyTimeout() { public static int getDefaultPacketReplyTimeout() {
return getDefaultReplyTimeout();
}
/**
* Sets the number of milliseconds to wait for a response from
* the server.
*
* @param timeout the milliseconds to wait for a response from the server
* @deprecated use {@link #setDefaultReplyTimeout(int)} instead.
*/
@Deprecated
public static void setDefaultPacketReplyTimeout(int timeout) {
setDefaultReplyTimeout(timeout);
}
/**
* Returns the number of milliseconds to wait for a response from
* the server. The default value is 5000 ms.
*
* @return the milliseconds to wait for a response from the server
*/
public static int getDefaultReplyTimeout() {
// The timeout value must be greater than 0 otherwise we will answer the default value // The timeout value must be greater than 0 otherwise we will answer the default value
if (defaultPacketReplyTimeout <= 0) { if (defaultPacketReplyTimeout <= 0) {
defaultPacketReplyTimeout = 5000; defaultPacketReplyTimeout = 5000;
@ -116,7 +140,7 @@ public final class SmackConfiguration {
* *
* @param timeout the milliseconds to wait for a response from the server * @param timeout the milliseconds to wait for a response from the server
*/ */
public static void setDefaultPacketReplyTimeout(int timeout) { public static void setDefaultReplyTimeout(int timeout) {
if (timeout <= 0) { if (timeout <= 0) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }

View File

@ -58,7 +58,7 @@ public class SmackException extends Exception {
/** /**
* Exception thrown always when there was no response to an request within the stanza(/packet) reply timeout of the used * Exception thrown always when there was no response to an request within the stanza(/packet) reply timeout of the used
* connection instance. You can modify (e.g. increase) the stanza(/packet) reply timeout with * connection instance. You can modify (e.g. increase) the stanza(/packet) reply timeout with
* {@link XMPPConnection#setPacketReplyTimeout(long)}. * {@link XMPPConnection#setReplyTimeout(long)}.
*/ */
public static final class NoResponseException extends SmackException { public static final class NoResponseException extends SmackException {
/** /**
@ -111,7 +111,7 @@ public class SmackException extends Exception {
} }
private static StringBuilder getWaitingFor(XMPPConnection connection) { private static StringBuilder getWaitingFor(XMPPConnection connection) {
final long replyTimeout = connection.getPacketReplyTimeout(); final long replyTimeout = connection.getReplyTimeout();
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 (~"

View File

@ -161,7 +161,7 @@ public class StanzaCollector {
* @throws InterruptedException * @throws InterruptedException
*/ */
public <P extends Stanza> P nextResult() throws InterruptedException { public <P extends Stanza> P nextResult() throws InterruptedException {
return nextResult(connection.getPacketReplyTimeout()); return nextResult(connection.getReplyTimeout());
} }
private volatile long waitStart; private volatile long waitStart;
@ -205,7 +205,7 @@ public class StanzaCollector {
*/ */
public <P extends Stanza> P nextResultOrThrow() throws NoResponseException, XMPPErrorException, public <P extends Stanza> P nextResultOrThrow() throws NoResponseException, XMPPErrorException,
InterruptedException, NotConnectedException { InterruptedException, NotConnectedException {
return nextResultOrThrow(connection.getPacketReplyTimeout()); return nextResultOrThrow(connection.getReplyTimeout());
} }
/** /**

View File

@ -234,7 +234,7 @@ public class SynchronizationPoint<E extends Exception> {
* @throws InterruptedException * @throws InterruptedException
*/ */
private void waitForConditionOrTimeout() throws InterruptedException { private void waitForConditionOrTimeout() throws InterruptedException {
long remainingWait = TimeUnit.MILLISECONDS.toNanos(connection.getPacketReplyTimeout()); long remainingWait = TimeUnit.MILLISECONDS.toNanos(connection.getReplyTimeout());
while (state == State.RequestSent || state == State.Initial) { while (state == State.RequestSent || state == State.Initial) {
if (remainingWait <= 0) { if (remainingWait <= 0) {
state = State.NoResponse; state = State.NoResponse;

View File

@ -410,7 +410,9 @@ public interface XMPPConnection {
* XMPPConnection instance. * XMPPConnection instance.
* *
* @return the stanza(/packet) reply timeout in milliseconds * @return the stanza(/packet) reply timeout in milliseconds
* @deprecated use {@link #getReplyTimeout()} instead.
*/ */
@Deprecated
public long getPacketReplyTimeout(); public long getPacketReplyTimeout();
/** /**
@ -418,9 +420,27 @@ public interface XMPPConnection {
* {@link NoResponseException} if no reply to a request was received within the timeout period. * {@link NoResponseException} if no reply to a request was received within the timeout period.
* *
* @param timeout the stanza(/packet) reply timeout in milliseconds * @param timeout the stanza(/packet) reply timeout in milliseconds
* @deprecated use {@link #setReplyTimeout(long)} instead.
*/ */
@Deprecated
public void setPacketReplyTimeout(long timeout); public void setPacketReplyTimeout(long timeout);
/**
* Returns the current value of the reply timeout in milliseconds for request for this
* XMPPConnection instance.
*
* @return the reply timeout in milliseconds
*/
public long getReplyTimeout();
/**
* Set the stanza(/packet) reply timeout in milliseconds. In most cases, Smack will throw a
* {@link NoResponseException} if no reply to a request was received within the timeout period.
*
* @param timeout for a reply in milliseconds
*/
public void setReplyTimeout(long timeout);
/** /**
* Get the connection counter of this XMPPConnection instance. Those can be used as ID to * Get the connection counter of this XMPPConnection instance. Those can be used as ID to
* identify the connection, but beware that the ID may not be unique if you create more then * identify the connection, but beware that the ID may not be unique if you create more then

View File

@ -0,0 +1,62 @@
/**
*
* Copyright 2017 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.filter;
import org.jivesoftware.smack.packet.Stanza;
import org.jxmpp.jid.Jid;
public abstract class AbstractJidTypeFilter implements StanzaFilter {
protected enum JidType {
entityFull,
entityBare,
domainFull,
domainBare,
;
}
private final JidType jidType;
protected AbstractJidTypeFilter(JidType jidType) {
this.jidType = jidType;
}
protected abstract Jid getJidToInspect(Stanza stanza);
@Override
public final boolean accept(Stanza stanza) {
final Jid jid = stanza.getFrom();
if (jid == null) {
return false;
}
switch (jidType) {
case entityFull:
return jid.isEntityFullJid();
case entityBare:
return jid.isEntityBareJid();
case domainFull:
return jid.isDomainFullJid();
case domainBare:
return jid.isDomainBareJid();
default:
throw new AssertionError();
}
}
}

View File

@ -0,0 +1,38 @@
/**
*
* Copyright 2017 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.filter;
import org.jivesoftware.smack.packet.Stanza;
import org.jxmpp.jid.Jid;
public final class FromTypeFilter extends AbstractJidTypeFilter {
public static final FromTypeFilter ENTITY_FULL_JID = new FromTypeFilter(JidType.entityFull);
public static final FromTypeFilter ENTITY_BARE_JID = new FromTypeFilter(JidType.entityBare);
public static final FromTypeFilter DOMAIN_FULL_JID = new FromTypeFilter(JidType.domainFull);
public static final FromTypeFilter DOMAIN_BARE_JID = new FromTypeFilter(JidType.domainBare);
private FromTypeFilter(JidType jidType) {
super(jidType);
}
@Override
protected Jid getJidToInspect(Stanza stanza) {
return stanza.getFrom();
}
}

View File

@ -0,0 +1,40 @@
/**
*
* Copyright 2017 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.filter;
import org.jivesoftware.smack.packet.Stanza;
import org.jxmpp.jid.Jid;
public final class ToTypeFilter extends AbstractJidTypeFilter {
public static final ToTypeFilter ENTITY_FULL_JID = new ToTypeFilter(JidType.entityFull);
public static final ToTypeFilter ENTITY_BARE_JID = new ToTypeFilter(JidType.entityBare);
public static final ToTypeFilter DOMAIN_FULL_JID = new ToTypeFilter(JidType.domainFull);
public static final ToTypeFilter DOMAIN_BARE_JID = new ToTypeFilter(JidType.domainBare);
public static final StanzaFilter ENTITY_FULL_OR_BARE_JID = new OrFilter(ENTITY_FULL_JID, ENTITY_BARE_JID);
private ToTypeFilter(JidType jidType) {
super(jidType);
}
@Override
protected Jid getJidToInspect(Stanza stanza) {
return stanza.getTo();
}
}

View File

@ -250,14 +250,16 @@ public class DNSUtil {
List<SRVRecord> bucket = buckets.get(priority); List<SRVRecord> bucket = buckets.get(priority);
int bucketSize; int bucketSize;
while ((bucketSize = bucket.size()) > 0) { while ((bucketSize = bucket.size()) > 0) {
int[] totals = new int[bucket.size()]; int[] totals = new int[bucketSize];
int running_total = 0; int running_total = 0;
int count = 0; int count = 0;
int zeroWeight = 1; int zeroWeight = 1;
for (SRVRecord r : bucket) { for (SRVRecord r : bucket) {
if (r.getWeight() > 0) if (r.getWeight() > 0) {
zeroWeight = 0; zeroWeight = 0;
break;
}
} }
for (SRVRecord r : bucket) { for (SRVRecord r : bucket) {

View File

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2013-2016 Florian Schmaus * Copyright 2013-2017 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.
@ -50,7 +50,7 @@ public abstract class DNSResolver {
public final HostAddress lookupHostAddress(String name, int port, List<HostAddress> failedAddresses, DnssecMode dnssecMode) { public final HostAddress lookupHostAddress(String name, int port, List<HostAddress> failedAddresses, DnssecMode dnssecMode) {
checkIfDnssecRequestedAndSupported(dnssecMode); checkIfDnssecRequestedAndSupported(dnssecMode);
List<InetAddress> inetAddresses = lookupHostAddress0(name, failedAddresses, dnssecMode); List<InetAddress> inetAddresses = lookupHostAddress0(name, failedAddresses, dnssecMode);
if (inetAddresses == null) { if (inetAddresses == null || inetAddresses.isEmpty()) {
return null; return null;
} }
return new HostAddress(name, port, inetAddresses); return new HostAddress(name, port, inetAddresses);

View File

@ -1,6 +1,6 @@
/** /**
* *
* Copyright © 2013-2016 Florian Schmaus * Copyright © 2013-2017 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.
@ -25,7 +25,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import org.jivesoftware.smack.SmackException.ConnectionException; import org.jivesoftware.smack.SmackException.ConnectionException;
import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.StringUtils;
public class HostAddress { public class HostAddress {
private final String fqdn; private final String fqdn;
@ -34,18 +34,17 @@ public class HostAddress {
private final List<InetAddress> inetAddresses; private final List<InetAddress> inetAddresses;
/** /**
* Creates a new HostAddress with the given FQDN. The port will be set to the default XMPP client port: 5222 * Creates a new HostAddress with the given FQDN.
* *
* @param fqdn Fully qualified domain name. * @param fqdn the optional fully qualified domain name (FQDN).
* @param port The port to connect on. * @param port The port to connect on.
* @throws IllegalArgumentException If the fqdn is null or port is out of valid range (0 - 65535). * @throws IllegalArgumentException If the port is out of valid range (0 - 65535).
*/ */
public HostAddress(String fqdn, int port, List<InetAddress> inetAddresses) { public HostAddress(String fqdn, int port, List<InetAddress> inetAddresses) {
Objects.requireNonNull(fqdn, "FQDN is null");
if (port < 0 || port > 65535) if (port < 0 || port > 65535)
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Port must be a 16-bit unsiged integer (i.e. between 0-65535. Port was: " + port); "Port must be a 16-bit unsiged integer (i.e. between 0-65535. Port was: " + port);
if (fqdn.charAt(fqdn.length() - 1) == '.') { if (StringUtils.isNotEmpty(fqdn) && fqdn.charAt(fqdn.length() - 1) == '.') {
this.fqdn = fqdn.substring(0, fqdn.length() - 1); this.fqdn = fqdn.substring(0, fqdn.length() - 1);
} }
else { else {
@ -59,7 +58,7 @@ public class HostAddress {
} }
public HostAddress(int port, InetAddress hostAddress) { public HostAddress(int port, InetAddress hostAddress) {
this("", port, Collections.singletonList(hostAddress)); this(null, port, Collections.singletonList(hostAddress));
} }
/** /**

View File

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2013-2016 Florian Schmaus * Copyright 2013-2017 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.
@ -19,6 +19,8 @@ package org.jivesoftware.smack.util.dns;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.List; import java.util.List;
import org.jivesoftware.smack.util.StringUtils;
/** /**
* A DNS SRV RR. * A DNS SRV RR.
* *
@ -43,6 +45,7 @@ public class SRVRecord extends HostAddress implements Comparable<SRVRecord> {
*/ */
public SRVRecord(String fqdn, int port, int priority, int weight, List<InetAddress> inetAddresses) { public SRVRecord(String fqdn, int port, int priority, int weight, List<InetAddress> inetAddresses) {
super(fqdn, port, inetAddresses); super(fqdn, port, inetAddresses);
StringUtils.requireNotNullOrEmpty(fqdn, "The FQDN must not be null");
if (weight < 0 || weight > 65535) if (weight < 0 || weight > 65535)
throw new IllegalArgumentException( throw new IllegalArgumentException(
"DNS SRV records weight must be a 16-bit unsiged integer (i.e. between 0-65535. Weight was: " "DNS SRV records weight must be a 16-bit unsiged integer (i.e. between 0-65535. Weight was: "

View File

@ -91,6 +91,7 @@ public class DummyConnection extends AbstractXMPPConnection {
protected void connectInternal() { protected void connectInternal() {
connected = true; connected = true;
saslFeatureReceived.reportSuccess(); saslFeatureReceived.reportSuccess();
tlsHandled.reportSuccess();
streamId = "dummy-" + new Random(new Date().getTime()).nextInt(); streamId = "dummy-" + new Random(new Date().getTime()).nextInt();
if (reconnect) { if (reconnect) {

View File

@ -28,7 +28,7 @@ public class SmackConfigurationTest {
@Test @Test
public void testSmackConfiguration() { public void testSmackConfiguration() {
try { try {
SmackConfiguration.getDefaultPacketReplyTimeout(); SmackConfiguration.getDefaultReplyTimeout();
} catch (Throwable t) { } catch (Throwable t) {
fail("SmackConfiguration threw Throwable"); fail("SmackConfiguration threw Throwable");
} }
@ -40,7 +40,7 @@ public class SmackConfigurationTest {
@Ignore @Ignore
@Test @Test
public void smackConfigurationShouldNotCauseInitializationTest() { public void smackConfigurationShouldNotCauseInitializationTest() {
SmackConfiguration.getDefaultPacketReplyTimeout(); SmackConfiguration.getDefaultReplyTimeout();
// Only a call to SmackConfiguration.getVersion() should cause Smack to become initialized. // Only a call to SmackConfiguration.getVersion() should cause Smack to become initialized.
assertFalse(SmackConfiguration.isSmackInitialized()); assertFalse(SmackConfiguration.isSmackInitialized());

View File

@ -18,6 +18,6 @@ package org.jivesoftware.smackx.iot.data.element;
public class Constants { public class Constants {
public static final String IOT_SENSORDATA_NAMESPACE = "urn:xmpp:iot:data"; public static final String IOT_SENSORDATA_NAMESPACE = "urn:xmpp:iot:sensordata";
} }

View File

@ -28,8 +28,6 @@ import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatManager;
import org.jivesoftware.smack.chat.ChatMessageListener; import org.jivesoftware.smack.chat.ChatMessageListener;
import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.FromMatchesFilter; import org.jivesoftware.smack.filter.FromMatchesFilter;
@ -146,8 +144,10 @@ public class MultiUserChatLight {
* for the newly created chat. * for the newly created chat.
* @return new Chat for sending private messages to a given room occupant. * @return new Chat for sending private messages to a given room occupant.
*/ */
public Chat createPrivateChat(EntityJid occupant, ChatMessageListener listener) { @Deprecated
return ChatManager.getInstanceFor(connection).createChat(occupant, listener); // Do not re-use Chat API, which was designed for XMPP-IM 1:1 chats and not MUClight private chats.
public org.jivesoftware.smack.chat.Chat createPrivateChat(EntityJid occupant, ChatMessageListener listener) {
return org.jivesoftware.smack.chat.ChatManager.getInstanceFor(connection).createChat(occupant, listener);
} }
/** /**

View File

@ -0,0 +1,73 @@
/**
*
* Copyright 2017 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.chat2;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.Jid;
public final class Chat extends Manager {
private final EntityBareJid jid;
volatile EntityFullJid lockedResource;
Presence lastPresenceOfLockedResource;
Chat(final XMPPConnection connection, EntityBareJid jid) {
super(connection);
this.jid = jid;
}
public void send(CharSequence message) throws NotConnectedException, InterruptedException {
Message stanza = new Message();
stanza.setBody(message);
send(stanza);
}
public void send(Message message) throws NotConnectedException, InterruptedException {
switch (message.getType()) {
case normal:
case chat:
break;
default:
throw new IllegalArgumentException("Message must be of type 'normal' or 'chat'");
}
Jid to = lockedResource;
if (to == null) {
to = jid;
}
message.setTo(to);
connection().sendStanza(message);
}
public EntityBareJid getXmppAddressOfChatPartner() {
return jid;
}
void unlockResource() {
lockedResource = null;
lastPresenceOfLockedResource = null;
}
}

View File

@ -0,0 +1,237 @@
/**
*
* Copyright 2017 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.chat2;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.FromTypeFilter;
import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.MessageWithBodiesFilter;
import org.jivesoftware.smack.filter.OrFilter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.ToTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.roster.AbstractRosterListener;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smackx.xhtmlim.packet.XHTMLExtension;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.Jid;
/**
* A chat manager for 1:1 XMPP instant messaging chats.
* <p>
* This manager and the according {@link Chat} API implement "Resource Locking" (XEP-0296). Support for Carbon Copies
* (XEP-0280) will be added once the XEP has progressed from experimental.
* </p>
*
* @see <a href="https://xmpp.org/extensions/xep-0296.html">XEP-0296: Best Practices for Resource Locking</a>
*/
public final class ChatManager extends Manager {
private static final Map<XMPPConnection, ChatManager> INSTANCES = new WeakHashMap<>();
public static synchronized ChatManager getInstanceFor(XMPPConnection connection) {
ChatManager chatManager = INSTANCES.get(connection);
if (chatManager == null) {
chatManager = new ChatManager(connection);
INSTANCES.put(connection, chatManager);
}
return chatManager;
}
// @FORMATTER:OFF
private static final StanzaFilter MESSAGE_FILTER = new AndFilter(
MessageTypeFilter.NORMAL_OR_CHAT,
new OrFilter(MessageWithBodiesFilter.INSTANCE), new StanzaExtensionFilter(XHTMLExtension.ELEMENT, XHTMLExtension.NAMESPACE)
);
private static final StanzaFilter OUTGOING_MESSAGE_FILTER = new AndFilter(
MESSAGE_FILTER,
ToTypeFilter.ENTITY_FULL_OR_BARE_JID
);
private static final StanzaFilter INCOMING_MESSAGE_FILTER = new AndFilter(
MESSAGE_FILTER,
FromTypeFilter.ENTITY_FULL_JID
);
// @FORMATTER:ON
private final Map<EntityBareJid, Chat> chats = new ConcurrentHashMap<>();
private final Set<IncomingChatMessageListener> incomingListeners = new CopyOnWriteArraySet<>();
private final Set<OutgoingChatMessageListener> outgoingListeners = new CopyOnWriteArraySet<>();
private boolean xhtmlIm;
private ChatManager(final XMPPConnection connection) {
super(connection);
connection.addSyncStanzaListener(new StanzaListener() {
@Override
public void processStanza(Stanza stanza) {
Message message = (Message) stanza;
if (!shouldAcceptMessage(message)) {
return;
}
final Jid from = message.getFrom();
final EntityFullJid fullFrom = from.asEntityFullJidOrThrow();
final EntityBareJid bareFrom = fullFrom.asEntityBareJid();
final Chat chat = chatWith(bareFrom);
chat.lockedResource = fullFrom;
for (IncomingChatMessageListener listener : incomingListeners) {
listener.newIncomingMessage(bareFrom, message, chat);
}
}
}, INCOMING_MESSAGE_FILTER);
connection.addPacketInterceptor(new StanzaListener() {
@Override
public void processStanza(Stanza stanza) throws NotConnectedException, InterruptedException {
Message message = (Message) stanza;
if (!shouldAcceptMessage(message)) {
return;
}
final EntityBareJid to = message.getTo().asEntityBareJidOrThrow();
final Chat chat = chatWith(to);
for (OutgoingChatMessageListener listener : outgoingListeners) {
listener.newOutgoingMessage(to, message, chat);
}
}
}, OUTGOING_MESSAGE_FILTER);
Roster roster = Roster.getInstanceFor(connection);
roster.addRosterListener(new AbstractRosterListener() {
@Override
public void presenceChanged(Presence presence) {
final Jid from = presence.getFrom();
final EntityBareJid bareFrom = from.asEntityBareJidIfPossible();
if (bareFrom == null) {
return;
}
final Chat chat = chats.get(bareFrom);
if (chat == null) {
return;
}
if (chat.lockedResource == null) {
// According to XEP-0296, no action is required for resource locking upon receiving a presence if no
// resource is currently locked.
return;
}
final EntityFullJid fullFrom = from.asEntityFullJidIfPossible();
if (!chat.lockedResource.equals(fullFrom)) {
return;
}
if (chat.lastPresenceOfLockedResource == null) {
// We have no last known presence from the locked resource.
chat.lastPresenceOfLockedResource = presence;
return;
}
if (chat.lastPresenceOfLockedResource.getMode() != presence.getMode()
|| chat.lastPresenceOfLockedResource.getType() != presence.getType()) {
chat.unlockResource();
}
}
});
}
private boolean shouldAcceptMessage(Message message) {
if (!message.getBodies().isEmpty()) {
return true;
}
// Message has no XMPP-IM bodies, abort here if xhtmlIm is not enabled.
if (!xhtmlIm) {
return false;
}
XHTMLExtension xhtmlExtension = XHTMLExtension.from(message);
if (xhtmlExtension == null) {
// Message has no XHTML-IM extension, abort.
return false;
}
return true;
}
public boolean addListener(IncomingChatMessageListener listener) {
return incomingListeners.add(listener);
}
public boolean removeListener(IncomingChatMessageListener listener) {
return incomingListeners.remove(listener);
}
public boolean addListener(OutgoingChatMessageListener listener) {
return outgoingListeners.add(listener);
}
public boolean removeOutoingLIstener(OutgoingChatMessageListener listener) {
return outgoingListeners.remove(listener);
}
/**
* Start a new or retrieve the existing chat with <code>jid</code>.
*
* @param jid the XMPP address of the other entity to chat with.
* @return the Chat API for the given XMPP address.
*/
public Chat chatWith(EntityBareJid jid) {
Chat chat = chats.get(jid);
if (chat == null) {
synchronized (chats) {
// Double-checked locking.
chat = chats.get(jid);
if (chat != null) {
return chat;
}
chat = new Chat(connection(), jid);
chats.put(jid, chat);
}
}
return chat;
}
/**
* Also notify about messages containing XHTML-IM.
*
* @param xhtmlIm
*/
public void setXhmtlImEnabled(boolean xhtmlIm) {
this.xhtmlIm = xhtmlIm;
}
}

View File

@ -0,0 +1,26 @@
/**
*
* Copyright 2017 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.chat2;
import org.jivesoftware.smack.packet.Message;
import org.jxmpp.jid.EntityBareJid;
public interface IncomingChatMessageListener {
void newIncomingMessage(EntityBareJid from, Message message, Chat chat);
}

View File

@ -0,0 +1,26 @@
/**
*
* Copyright 2017 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.chat2;
import org.jivesoftware.smack.packet.Message;
import org.jxmpp.jid.EntityBareJid;
public interface OutgoingChatMessageListener {
void newOutgoingMessage(EntityBareJid to, Message message, Chat chat);
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright 2017 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Smack's new improved API for 1:1 chats.
*/
package org.jivesoftware.smack.chat2;

View File

@ -17,7 +17,6 @@
package org.jivesoftware.smackx.chatstates; package org.jivesoftware.smackx.chatstates;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatMessageListener; import org.jivesoftware.smack.chat.ChatMessageListener;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
@ -35,5 +34,7 @@ public interface ChatStateListener extends ChatMessageListener {
* @param state the new state of the participant. * @param state the new state of the participant.
* @param message the message carrying the chat state. * @param message the message carrying the chat state.
*/ */
void stateChanged(Chat chat, ChatState state, Message message); // TODO Migrate to new chat2 API on Smack 4.3.
@SuppressWarnings("deprecation")
void stateChanged(org.jivesoftware.smack.chat.Chat chat, ChatState state, Message message);
} }

View File

@ -24,8 +24,6 @@ import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatManager;
import org.jivesoftware.smack.chat.ChatManagerListener; import org.jivesoftware.smack.chat.ChatManagerListener;
import org.jivesoftware.smack.chat.ChatMessageListener; import org.jivesoftware.smack.chat.ChatMessageListener;
import org.jivesoftware.smack.filter.NotFilter; import org.jivesoftware.smack.filter.NotFilter;
@ -49,6 +47,8 @@ import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
* @see org.jivesoftware.smackx.chatstates.ChatState * @see org.jivesoftware.smackx.chatstates.ChatState
* @see org.jivesoftware.smackx.chatstates.packet.ChatStateExtension * @see org.jivesoftware.smackx.chatstates.packet.ChatStateExtension
*/ */
// TODO Migrate to new chat2 API on Smack 4.3.
@SuppressWarnings("deprecation")
public final class ChatStateManager extends Manager { public final class ChatStateManager extends Manager {
public static final String NAMESPACE = "http://jabber.org/protocol/chatstates"; public static final String NAMESPACE = "http://jabber.org/protocol/chatstates";
@ -79,13 +79,13 @@ public final class ChatStateManager extends Manager {
/** /**
* Maps chat to last chat state. * Maps chat to last chat state.
*/ */
private final Map<Chat, ChatState> chatStates = new WeakHashMap<Chat, ChatState>(); private final Map<org.jivesoftware.smack.chat.Chat, ChatState> chatStates = new WeakHashMap<>();
private final ChatManager chatManager; private final org.jivesoftware.smack.chat.ChatManager chatManager;
private ChatStateManager(XMPPConnection connection) { private ChatStateManager(XMPPConnection connection) {
super(connection); super(connection);
chatManager = ChatManager.getInstanceFor(connection); chatManager = org.jivesoftware.smack.chat.ChatManager.getInstanceFor(connection);
chatManager.addOutgoingMessageInterceptor(outgoingInterceptor, filter); chatManager.addOutgoingMessageInterceptor(outgoingInterceptor, filter);
chatManager.addChatListener(incomingInterceptor); chatManager.addChatListener(incomingInterceptor);
@ -104,7 +104,7 @@ public final class ChatStateManager extends Manager {
* @throws NotConnectedException * @throws NotConnectedException
* @throws InterruptedException * @throws InterruptedException
*/ */
public void setCurrentState(ChatState newState, Chat chat) throws NotConnectedException, InterruptedException { public void setCurrentState(ChatState newState, org.jivesoftware.smack.chat.Chat chat) throws NotConnectedException, InterruptedException {
if(chat == null || newState == null) { if(chat == null || newState == null) {
throw new IllegalArgumentException("Arguments cannot be null."); throw new IllegalArgumentException("Arguments cannot be null.");
} }
@ -133,7 +133,7 @@ public final class ChatStateManager extends Manager {
return connection().hashCode(); return connection().hashCode();
} }
private synchronized boolean updateChatState(Chat chat, ChatState newState) { private synchronized boolean updateChatState(org.jivesoftware.smack.chat.Chat chat, ChatState newState) {
ChatState lastChatState = chatStates.get(chat); ChatState lastChatState = chatStates.get(chat);
if (lastChatState != newState) { if (lastChatState != newState) {
chatStates.put(chat, newState); chatStates.put(chat, newState);
@ -142,7 +142,7 @@ public final class ChatStateManager extends Manager {
return false; return false;
} }
private static void fireNewChatState(Chat chat, ChatState state, Message message) { private static void fireNewChatState(org.jivesoftware.smack.chat.Chat chat, ChatState state, Message message) {
for (ChatMessageListener listener : chat.getListeners()) { for (ChatMessageListener listener : chat.getListeners()) {
if (listener instanceof ChatStateListener) { if (listener instanceof ChatStateListener) {
((ChatStateListener) listener).stateChanged(chat, state, message); ((ChatStateListener) listener).stateChanged(chat, state, message);
@ -154,7 +154,7 @@ public final class ChatStateManager extends Manager {
@Override @Override
public void processMessage(Message message) { public void processMessage(Message message) {
Chat chat = chatManager.getThreadChat(message.getThread()); org.jivesoftware.smack.chat.Chat chat = chatManager.getThreadChat(message.getThread());
if (chat == null) { if (chat == null) {
return; return;
} }
@ -166,11 +166,11 @@ public final class ChatStateManager extends Manager {
private class IncomingMessageInterceptor implements ChatManagerListener, ChatMessageListener { private class IncomingMessageInterceptor implements ChatManagerListener, ChatMessageListener {
public void chatCreated(final Chat chat, boolean createdLocally) { public void chatCreated(final org.jivesoftware.smack.chat.Chat chat, boolean createdLocally) {
chat.addMessageListener(this); chat.addMessageListener(this);
} }
public void processMessage(Chat chat, Message message) { public void processMessage(org.jivesoftware.smack.chat.Chat chat, Message message) {
ExtensionElement extension = message.getExtension(NAMESPACE); ExtensionElement extension = message.getExtension(NAMESPACE);
if (extension == null) { if (extension == null) {
return; return;

View File

@ -96,7 +96,7 @@ public abstract class StreamNegotiator {
final String eventKey = initiation.getFrom().toString() + '\t' + initiation.getSessionID(); final String eventKey = initiation.getFrom().toString() + '\t' + initiation.getSessionID();
IQ streamMethodInitiation; IQ streamMethodInitiation;
try { try {
streamMethodInitiation = initationSetEvents.performActionAndWaitForEvent(eventKey, connection.getPacketReplyTimeout(), new Callback<NotConnectedException>() { streamMethodInitiation = initationSetEvents.performActionAndWaitForEvent(eventKey, connection.getReplyTimeout(), new Callback<NotConnectedException>() {
@Override @Override
public void action() throws NotConnectedException { public void action() throws NotConnectedException {
try { try {

View File

@ -335,12 +335,19 @@ public final class AccountManager extends Manager {
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
XMPPConnection connection = connection(); XMPPConnection connection = connection();
ExtensionElement extensionElement = connection.getFeature(Registration.ELEMENT, Registration.NAMESPACE); ExtensionElement extensionElement = connection.getFeature(Registration.Feature.ELEMENT,
Registration.Feature.NAMESPACE);
if (extensionElement != null) { if (extensionElement != null) {
return true; return true;
} }
return ServiceDiscoveryManager.getInstanceFor(connection).serverSupportsFeature(Registration.NAMESPACE); // Fallback to disco#info only if this connection is authenticated, as otherwise we won't have an full JID and
// won't be able to do IQs.
if (connection.isAuthenticated()) {
return ServiceDiscoveryManager.getInstanceFor(connection).serverSupportsFeature(Registration.NAMESPACE);
}
return false;
} }
/** /**

View File

@ -37,8 +37,6 @@ import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatManager;
import org.jivesoftware.smack.chat.ChatMessageListener; import org.jivesoftware.smack.chat.ChatMessageListener;
import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.FromMatchesFilter; import org.jivesoftware.smack.filter.FromMatchesFilter;
@ -357,7 +355,7 @@ public class MultiUserChat {
* @since 4.2 * @since 4.2
*/ */
public MucEnterConfiguration.Builder getEnterConfigurationBuilder(Resourcepart nickname) { public MucEnterConfiguration.Builder getEnterConfigurationBuilder(Resourcepart nickname) {
return new MucEnterConfiguration.Builder(nickname, connection.getPacketReplyTimeout()); return new MucEnterConfiguration.Builder(nickname, connection.getReplyTimeout());
} }
/** /**
@ -1847,8 +1845,11 @@ public class MultiUserChat {
* created chat. * created chat.
* @return new Chat for sending private messages to a given room occupant. * @return new Chat for sending private messages to a given room occupant.
*/ */
public Chat createPrivateChat(EntityFullJid occupant, ChatMessageListener listener) { // TODO This should be made new not using chat.Chat. Private MUC chats are different from XMPP-IM 1:1 chats in to many ways.
return ChatManager.getInstanceFor(connection).createChat(occupant, listener); // API sketch: PrivateMucChat createPrivateChat(Resourcepart nick)
@SuppressWarnings("deprecation")
public org.jivesoftware.smack.chat.Chat createPrivateChat(EntityFullJid occupant, ChatMessageListener listener) {
return org.jivesoftware.smack.chat.ChatManager.getInstanceFor(connection).createChat(occupant, listener);
} }
/** /**

View File

@ -158,12 +158,14 @@ public class OfflineMessageManager {
return nodes.contains(info.getNode()); return nodes.contains(info.getNode());
} }
}); });
int pendingNodes = nodes.size();
StanzaCollector messageCollector = connection.createStanzaCollector(messageFilter); StanzaCollector messageCollector = connection.createStanzaCollector(messageFilter);
try { try {
connection.createStanzaCollectorAndSend(request).nextResultOrThrow(); connection.createStanzaCollectorAndSend(request).nextResultOrThrow();
// Collect the received offline messages // Collect the received offline messages
Message message = messageCollector.nextResult(); Message message = messageCollector.nextResult();
while (message != null) { while (message != null && pendingNodes > 0) {
pendingNodes--;
messages.add(message); messages.add(message);
message = messageCollector.nextResult(); message = messageCollector.nextResult();
} }

View File

@ -186,7 +186,7 @@ public final class PingManager extends Manager {
* @throws InterruptedException * @throws InterruptedException
*/ */
public boolean ping(Jid jid) throws NotConnectedException, NoResponseException, InterruptedException { public boolean ping(Jid jid) throws NotConnectedException, NoResponseException, InterruptedException {
return ping(jid, connection().getPacketReplyTimeout()); return ping(jid, connection().getReplyTimeout());
} }
/** /**
@ -231,7 +231,7 @@ public final class PingManager extends Manager {
* @throws InterruptedException * @throws InterruptedException
*/ */
public boolean pingMyServer(boolean notifyListeners) throws NotConnectedException, InterruptedException { public boolean pingMyServer(boolean notifyListeners) throws NotConnectedException, InterruptedException {
return pingMyServer(notifyListeners, connection().getPacketReplyTimeout()); return pingMyServer(notifyListeners, connection().getReplyTimeout());
} }
/** /**

View File

@ -248,7 +248,7 @@ public class PingTest extends InitExtensions {
*/ */
private static DummyConnection getAuthenticatedDummyConnectionWithoutIqReplies() throws SmackException, IOException, XMPPException, InterruptedException { private static DummyConnection getAuthenticatedDummyConnectionWithoutIqReplies() throws SmackException, IOException, XMPPException, InterruptedException {
DummyConnection con = new DummyConnection(); DummyConnection con = new DummyConnection();
con.setPacketReplyTimeout(500); con.setReplyTimeout(500);
con.connect(); con.connect();
con.login(); con.login();
return con; return con;

View File

@ -88,7 +88,7 @@ public class ConfigureFormTest
Node node = mgr.getNode("princely_musings"); Node node = mgr.getNode("princely_musings");
SmackConfiguration.setDefaultPacketReplyTimeout(100); SmackConfiguration.setDefaultReplyTimeout(100);
con.setTimeout(); con.setTimeout();
node.getNodeConfiguration(); node.getNodeConfiguration();

View File

@ -36,7 +36,9 @@ import java.util.concurrent.CopyOnWriteArraySet;
* sender. * sender.
* *
* @author Matt Tucker * @author Matt Tucker
* @deprecated use <code>org.jivesoftware.smack.chat2.Chat</code> from <code>smack-extensions</code> instead.
*/ */
@Deprecated
public class Chat { public class Chat {
private ChatManager chatManager; private ChatManager chatManager;

View File

@ -52,7 +52,9 @@ import org.jxmpp.jid.EntityJid;
* made aware of new chats, register a listener by calling {@link #addChatListener(ChatManagerListener)}. * made aware of new chats, register a listener by calling {@link #addChatListener(ChatManagerListener)}.
* *
* @author Alexander Wenckus * @author Alexander Wenckus
* @deprecated use <code>org.jivesoftware.smack.chat2.ChatManager</code> from <code>smack-extensions</code> instead.
*/ */
@Deprecated
public final class ChatManager extends Manager{ public final class ChatManager extends Manager{
private static final Logger LOGGER = Logger.getLogger(ChatManager.class.getName()); private static final Logger LOGGER = Logger.getLogger(ChatManager.class.getName());

View File

@ -30,5 +30,6 @@ public interface ChatManagerListener {
* @param chat the chat that was created. * @param chat the chat that was created.
* @param createdLocally true if the chat was created by the local user and false if it wasn't. * @param createdLocally true if the chat was created by the local user and false if it wasn't.
*/ */
@SuppressWarnings("deprecation")
void chatCreated(Chat chat, boolean createdLocally); void chatCreated(Chat chat, boolean createdLocally);
} }

View File

@ -20,5 +20,6 @@ package org.jivesoftware.smack.chat;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
public interface ChatMessageListener { public interface ChatMessageListener {
@SuppressWarnings("deprecation")
void processMessage(Chat chat, Message message); void processMessage(Chat chat, Message message);
} }

View File

@ -473,7 +473,7 @@ public final class Roster extends Manager {
} }
protected boolean waitUntilLoaded() throws InterruptedException { protected boolean waitUntilLoaded() throws InterruptedException {
long waitTime = connection().getPacketReplyTimeout(); long waitTime = connection().getReplyTimeout();
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
while (!isLoaded()) { while (!isLoaded()) {
if (waitTime <= 0) { if (waitTime <= 0) {

View File

@ -24,7 +24,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import org.jivesoftware.smack.DummyConnection; import org.jivesoftware.smack.DummyConnection;
import org.jivesoftware.smack.chat.ChatManager.MatchMode;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Message.Type; import org.jivesoftware.smack.packet.Message.Type;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
@ -35,6 +34,7 @@ import org.junit.Test;
import org.jxmpp.jid.Jid; import org.jxmpp.jid.Jid;
import org.jxmpp.jid.JidTestUtil; import org.jxmpp.jid.JidTestUtil;
@SuppressWarnings("deprecation")
public class ChatConnectionTest { public class ChatConnectionTest {
private DummyConnection dc; private DummyConnection dc;
@ -46,7 +46,7 @@ public class ChatConnectionTest {
public void setUp() throws Exception { public void setUp() throws Exception {
// Defaults // Defaults
ChatManager.setDefaultIsNormalIncluded(true); ChatManager.setDefaultIsNormalIncluded(true);
ChatManager.setDefaultMatchMode(MatchMode.BARE_JID); ChatManager.setDefaultMatchMode(ChatManager.MatchMode.BARE_JID);
dc = DummyConnection.newConnectedDummyConnection(); dc = DummyConnection.newConnectedDummyConnection();
cm = ChatManager.getInstanceFor(dc); cm = ChatManager.getInstanceFor(dc);
@ -77,14 +77,14 @@ public class ChatConnectionTest {
@Test @Test
public void validateDefaultSetMatchModeNone() { public void validateDefaultSetMatchModeNone() {
ChatManager.setDefaultMatchMode(MatchMode.NONE); ChatManager.setDefaultMatchMode(ChatManager.MatchMode.NONE);
assertEquals(MatchMode.NONE, ChatManager.getInstanceFor(new DummyConnection()).getMatchMode()); assertEquals(ChatManager.MatchMode.NONE, ChatManager.getInstanceFor(new DummyConnection()).getMatchMode());
} }
@Test @Test
public void validateDefaultSetMatchModeEntityBareJid() { public void validateDefaultSetMatchModeEntityBareJid() {
ChatManager.setDefaultMatchMode(MatchMode.BARE_JID); ChatManager.setDefaultMatchMode(ChatManager.MatchMode.BARE_JID);
assertEquals(MatchMode.BARE_JID, ChatManager.getInstanceFor(new DummyConnection()).getMatchMode()); assertEquals(ChatManager.MatchMode.BARE_JID, ChatManager.getInstanceFor(new DummyConnection()).getMatchMode());
} }
@Test @Test
@ -138,7 +138,7 @@ public class ChatConnectionTest {
// No thread behaviour // No thread behaviour
@Test @Test
public void chatMatchedOnJIDWhenNoThreadBareMode() { public void chatMatchedOnJIDWhenNoThreadBareMode() {
// MatchMode.BARE_JID is the default, so setting required. // ChatManager.MatchMode.BARE_JID is the default, so setting required.
TestMessageListener msgListener = new TestMessageListener(); TestMessageListener msgListener = new TestMessageListener();
TestChatManagerListener listener = new TestChatManagerListener(msgListener); TestChatManagerListener listener = new TestChatManagerListener(msgListener);
cm.addChatListener(listener); cm.addChatListener(listener);
@ -162,7 +162,7 @@ public class ChatConnectionTest {
public void chatMatchedOnJIDWhenNoThreadJidMode() { public void chatMatchedOnJIDWhenNoThreadJidMode() {
TestMessageListener msgListener = new TestMessageListener(); TestMessageListener msgListener = new TestMessageListener();
TestChatManagerListener listener = new TestChatManagerListener(msgListener); TestChatManagerListener listener = new TestChatManagerListener(msgListener);
cm.setMatchMode(MatchMode.SUPPLIED_JID); cm.setMatchMode(ChatManager.MatchMode.SUPPLIED_JID);
cm.addChatListener(listener); cm.addChatListener(listener);
Stanza incomingChat = createChatPacket(null, true); Stanza incomingChat = createChatPacket(null, true);
processServerMessage(incomingChat); processServerMessage(incomingChat);
@ -188,7 +188,7 @@ public class ChatConnectionTest {
public void chatMatchedOnJIDWhenNoThreadNoneMode() { public void chatMatchedOnJIDWhenNoThreadNoneMode() {
TestMessageListener msgListener = new TestMessageListener(); TestMessageListener msgListener = new TestMessageListener();
TestChatManagerListener listener = new TestChatManagerListener(msgListener); TestChatManagerListener listener = new TestChatManagerListener(msgListener);
cm.setMatchMode(MatchMode.NONE); cm.setMatchMode(ChatManager.MatchMode.NONE);
cm.addChatListener(listener); cm.addChatListener(listener);
Stanza incomingChat = createChatPacket(null, true); Stanza incomingChat = createChatPacket(null, true);
processServerMessage(incomingChat); processServerMessage(incomingChat);

View File

@ -75,7 +75,7 @@ public class RosterTest extends InitSmackIm {
rosterListener = new TestRosterListener(); rosterListener = new TestRosterListener();
roster = Roster.getInstanceFor(connection); roster = Roster.getInstanceFor(connection);
roster.addRosterListener(rosterListener); roster.addRosterListener(rosterListener);
connection.setPacketReplyTimeout(1000 * 60 * 5); connection.setReplyTimeout(1000 * 60 * 5);
} }
@After @After

View File

@ -61,7 +61,7 @@ public class SubscriptionPreApprovalTest extends InitSmackIm {
rosterListener = new TestRosterListener(); rosterListener = new TestRosterListener();
roster = Roster.getInstanceFor(connection); roster = Roster.getInstanceFor(connection);
roster.addRosterListener(rosterListener); roster.addRosterListener(rosterListener);
connection.setPacketReplyTimeout(1000 * 60 * 5); connection.setReplyTimeout(1000 * 60 * 5);
} }
@After @After

View File

@ -133,7 +133,7 @@ public class SmackIntegrationTestFramework {
SmackConfiguration.DEBUG = true; SmackConfiguration.DEBUG = true;
} }
if (config.replyTimeout > 0) { if (config.replyTimeout > 0) {
SmackConfiguration.setDefaultPacketReplyTimeout(config.replyTimeout); SmackConfiguration.setDefaultReplyTimeout(config.replyTimeout);
} }
if (config.securityMode != SecurityMode.required) { if (config.securityMode != SecurityMode.required) {
AccountManager.sensitiveOperationOverInsecureConnectionDefault(true); AccountManager.sensitiveOperationOverInsecureConnectionDefault(true);

View File

@ -31,8 +31,6 @@ import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatManager;
import org.jivesoftware.smack.chat.ChatManagerListener; import org.jivesoftware.smack.chat.ChatManagerListener;
import org.jivesoftware.smack.filter.ThreadFilter; import org.jivesoftware.smack.filter.ThreadFilter;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
@ -46,12 +44,14 @@ import org.jxmpp.stringprep.XmppStringprepException;
*/ */
public class ChatTest extends AbstractSmackIntegrationTest { public class ChatTest extends AbstractSmackIntegrationTest {
private final ChatManager chatManagerOne; @SuppressWarnings("deprecation")
private final org.jivesoftware.smack.chat.ChatManager chatManagerOne;
private boolean invoked; private boolean invoked;
@SuppressWarnings("deprecation")
public ChatTest(SmackIntegrationTestEnvironment environment) { public ChatTest(SmackIntegrationTestEnvironment environment) {
super(environment); super(environment);
chatManagerOne = ChatManager.getInstanceFor(conOne); chatManagerOne = org.jivesoftware.smack.chat.ChatManager.getInstanceFor(conOne);
} }
@BeforeClass @BeforeClass
@ -64,9 +64,10 @@ public class ChatTest extends AbstractSmackIntegrationTest {
JivePropertiesManager.setJavaObjectEnabled(false); JivePropertiesManager.setJavaObjectEnabled(false);
} }
@SuppressWarnings("deprecation")
@SmackIntegrationTest @SmackIntegrationTest
public void testProperties() throws XmppStringprepException, NotConnectedException, Exception { public void testProperties() throws XmppStringprepException, NotConnectedException, Exception {
Chat newChat = chatManagerOne.createChat(conTwo.getUser()); org.jivesoftware.smack.chat.Chat newChat = chatManagerOne.createChat(conTwo.getUser());
StanzaCollector collector = conTwo.createStanzaCollector(new ThreadFilter(newChat.getThreadID())); StanzaCollector collector = conTwo.createStanzaCollector(new ThreadFilter(newChat.getThreadID()));
Message msg = new Message(); Message msg = new Message();
@ -112,12 +113,13 @@ public class ChatTest extends AbstractSmackIntegrationTest {
getProperty(msg2, "birthdate")); getProperty(msg2, "birthdate"));
} }
@SuppressWarnings("deprecation")
@SmackIntegrationTest @SmackIntegrationTest
public void chatManagerTest() { public void chatManagerTest() {
ChatManagerListener listener = new ChatManagerListener() { ChatManagerListener listener = new ChatManagerListener() {
@Override @Override
public void chatCreated(Chat chat, boolean createdLocally) { public void chatCreated(org.jivesoftware.smack.chat.Chat chat, boolean createdLocally) {
invoked = true; invoked = true;
} }

View File

@ -104,7 +104,7 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest {
try { try {
rosterOne.createEntry(conTwo.getUser().asBareJid(), conTwosRosterName, null); rosterOne.createEntry(conTwo.getUser().asBareJid(), conTwosRosterName, null);
assertTrue(addedAndSubscribed.waitForResult(2 * connection.getPacketReplyTimeout())); assertTrue(addedAndSubscribed.waitForResult(2 * connection.getReplyTimeout()));
} }
finally { finally {
rosterTwo.removeSubscribeListener(subscribeListener); rosterTwo.removeSubscribeListener(subscribeListener);

View File

@ -26,8 +26,6 @@ import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.StanzaCollector; import org.jivesoftware.smack.StanzaCollector;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatManager;
import org.jivesoftware.smack.filter.ThreadFilter; import org.jivesoftware.smack.filter.ThreadFilter;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.xdata.packet.DataForm; import org.jivesoftware.smackx.xdata.packet.DataForm;
@ -51,7 +49,8 @@ public class FormTest extends AbstractSmackIntegrationTest {
* @throws InterruptedException * @throws InterruptedException
* @throws NotConnectedException * @throws NotConnectedException
*/ */
@SmackIntegrationTest @SuppressWarnings("deprecation")
@SmackIntegrationTest
public void testFilloutForm() throws NotConnectedException, InterruptedException { public void testFilloutForm() throws NotConnectedException, InterruptedException {
Form formToSend = new Form(DataForm.Type.form); Form formToSend = new Form(DataForm.Type.form);
formToSend.setInstructions( formToSend.setInstructions(
@ -88,7 +87,7 @@ public class FormTest extends AbstractSmackIntegrationTest {
formToSend.addField(field); formToSend.addField(field);
// Create the chats between the two participants // Create the chats between the two participants
Chat chat = ChatManager.getInstanceFor(conOne).createChat(conTwo.getUser(), null); org.jivesoftware.smack.chat.Chat chat = org.jivesoftware.smack.chat.ChatManager.getInstanceFor(conOne).createChat(conTwo.getUser(), null);
StanzaCollector collector = conOne.createStanzaCollector( StanzaCollector collector = conOne.createStanzaCollector(
new ThreadFilter(chat.getThreadID())); new ThreadFilter(chat.getThreadID()));
StanzaCollector collector2 = conTwo.createStanzaCollector( StanzaCollector collector2 = conTwo.createStanzaCollector(

View File

@ -75,8 +75,8 @@ public class IoT {
final XMPPTCPConnection dataThingConnection = new XMPPTCPConnection(dataThingConnectionConfiguration); final XMPPTCPConnection dataThingConnection = new XMPPTCPConnection(dataThingConnectionConfiguration);
final XMPPTCPConnection readingThingConnection = new XMPPTCPConnection(readingThingConnectionConfiguration); final XMPPTCPConnection readingThingConnection = new XMPPTCPConnection(readingThingConnectionConfiguration);
dataThingConnection.setPacketReplyTimeout(TIMEOUT); dataThingConnection.setReplyTimeout(TIMEOUT);
readingThingConnection.setPacketReplyTimeout(TIMEOUT); readingThingConnection.setReplyTimeout(TIMEOUT);
dataThingConnection.setUseStreamManagement(false); dataThingConnection.setUseStreamManagement(false);
readingThingConnection.setUseStreamManagement(false); readingThingConnection.setUseStreamManagement(false);

View File

@ -89,7 +89,7 @@ public class TlsTest {
XMPPTCPConnection connection = new XMPPTCPConnection(builder.build()); XMPPTCPConnection connection = new XMPPTCPConnection(builder.build());
connection.setPacketReplyTimeout(20000); connection.setReplyTimeout(20000);
try { try {
connection.connect().login(); connection.connect().login();

View File

@ -20,8 +20,10 @@ import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set;
import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode; import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode;
import org.jivesoftware.smack.initializer.SmackInitializer; import org.jivesoftware.smack.initializer.SmackInitializer;
@ -126,10 +128,28 @@ public class MiniDnsResolver extends DNSResolver implements SmackInitializer {
return null; return null;
} }
List<InetAddress> inetAddresses = new ArrayList<>(aResult.getAnswers().size() // TODO: Use ResolverResult.getAnswersOrEmptySet() once we updated MiniDNS.
+ aaaaResult.getAnswers().size()); Set<A> aResults;
if (aResult.wasSuccessful()) {
aResults = aResult.getAnswers();
}
else {
aResults = Collections.emptySet();
}
for (A a : aResult.getAnswers()) { // TODO: Use ResolverResult.getAnswersOrEmptySet() once we updated MiniDNS.
Set<AAAA> aaaaResults;
if (aaaaResult.wasSuccessful()) {
aaaaResults = aaaaResult.getAnswers();
}
else {
aaaaResults = Collections.emptySet();
}
List<InetAddress> inetAddresses = new ArrayList<>(aResults.size()
+ aaaaResults.size());
for (A a : aResults) {
InetAddress inetAddress; InetAddress inetAddress;
try { try {
inetAddress = InetAddress.getByAddress(a.getIp()); inetAddress = InetAddress.getByAddress(a.getIp());
@ -139,7 +159,7 @@ public class MiniDnsResolver extends DNSResolver implements SmackInitializer {
} }
inetAddresses.add(inetAddress); inetAddresses.add(inetAddress);
} }
for (AAAA aaaa : aaaaResult.getAnswers()) { for (AAAA aaaa : aaaaResults) {
InetAddress inetAddress; InetAddress inetAddress;
try { try {
inetAddress = InetAddress.getByAddress(name, aaaa.getIp()); inetAddress = InetAddress.getByAddress(name, aaaa.getIp());

View File

@ -46,6 +46,7 @@ import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.StartTls; import org.jivesoftware.smack.packet.StartTls;
import org.jivesoftware.smack.packet.StreamError;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements; import org.jivesoftware.smack.sasl.packet.SaslStreamElements;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Challenge; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Challenge;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
@ -593,6 +594,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
} }
failedAddresses.add(hostAddress); failedAddresses.add(hostAddress);
} else { } else {
socket = socketFactory.createSocket();
StringUtils.requireNotNullOrEmpty(host, "Host of HostAddress " + hostAddress + " must not be null when using a Proxy");
final String hostAndPort = host + " at port " + port; final String hostAndPort = host + " at port " + port;
LOGGER.finer("Trying to establish TCP connection via Proxy to " + hostAndPort); LOGGER.finer("Trying to establish TCP connection via Proxy to " + hostAndPort);
try { try {
@ -922,13 +925,19 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
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) {
notifyConnectionError(new SecurityRequiredByServerException()); SmackException smackException = new SecurityRequiredByServerException();
tlsHandled.reportFailure(smackException);
notifyConnectionError(smackException);
return; return;
} }
if (config.getSecurityMode() != ConnectionConfiguration.SecurityMode.disabled) { if (config.getSecurityMode() != ConnectionConfiguration.SecurityMode.disabled) {
sendNonza(new StartTls()); sendNonza(new StartTls());
} else {
tlsHandled.reportSuccess();
} }
} else {
tlsHandled.reportSuccess();
} }
if (getSASLAuthentication().authenticationSuccessful()) { if (getSASLAuthentication().authenticationSuccessful()) {
@ -1027,7 +1036,13 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
} }
break; break;
case "error": case "error":
throw new StreamErrorException(PacketParserUtils.parseStreamError(parser)); StreamError streamError = PacketParserUtils.parseStreamError(parser);
saslFeatureReceived.reportFailure(new StreamErrorException(streamError));
// Mark the tlsHandled sync point as success, we will use the saslFeatureReceived sync
// point to report the error, which is checked immediately after tlsHandled in
// connectInternal().
tlsHandled.reportSuccess();
throw new StreamErrorException(streamError);
case "features": case "features":
parseFeatures(parser); parseFeatures(parser);
break; break;
@ -1039,9 +1054,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
openStream(); openStream();
} }
catch (Exception e) { catch (Exception e) {
// We report any failure regarding TLS in the second stage of XMPP SmackException smackException = new SmackException(e);
// connection establishment, namely the SASL authentication tlsHandled.reportFailure(smackException);
saslFeatureReceived.reportFailure(new SmackException(e));
throw e; throw e;
} }
break; break;