1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-11-22 20:12:07 +01:00

Compare commits

...

25 commits

Author SHA1 Message Date
Florian Schmaus
0cd3318b12
Merge pull request #301 from adiaholic/JIRA836
Saving an instance of ServiceDiscoveryManager in MultiUserChatManager.
2019-03-24 11:39:20 +01:00
Florian Schmaus
a23adfab6e Merge branch '4.3' 2019-03-24 00:16:24 +01:00
Oliver Mihatsch
007a04c4fe Better error messages when using a Proxy to connect to the XMPP server. 2019-03-24 00:12:32 +01:00
Georg Lukas
8e88cd2e31 Proxy mode: add failed address on error 2019-03-24 00:11:01 +01:00
Oliver Mihatsch
3450ffad2b Do not use "CONNECT" in the Host header field. 2019-03-24 00:09:54 +01:00
Florian Schmaus
dfae6770af Merge branch '4.3' 2019-03-22 21:56:15 +01:00
Florian Schmaus
653d9dbba7 smack-bosh: Limit jbosh to the 0.9 series
akin to version.gradle
2019-03-22 21:55:26 +01:00
Mohsen Hariri
c83d717a26 Allow adding custom HTTP headers to bosh communications 2019-03-22 21:49:06 +01:00
Florian Schmaus
7723230589
Merge pull request #304 from vanitasvitae/mr_template
Add Github Pull Request Template
2019-03-20 20:31:25 +01:00
Florian Schmaus
a9b88d7517 Show all exceptions ErrorsWhileSendingOrReceivingException message 2019-03-20 20:16:50 +01:00
Florian Schmaus
1dd68336eb
Merge pull request #305 from mwild1/patch-1
Minor fix to integrationtest.md formatting
2019-03-20 20:13:50 +01:00
3521346c90
Add Github Pull Request Template 2019-03-20 20:05:43 +01:00
Florian Schmaus
989dbb14e8
Merge pull request #296 from MarcelHeckel/PingManager_ConnectionOkOnAnyPacket
PingManager: con. assumed ok for any packed within the ping interval
2019-03-20 20:00:54 +01:00
Matthew Wild
b1cd5066f6
Minor fix to integrationtest.md formatting
The preceding paragraph was being treated as part of the header.
2019-03-19 08:59:42 +00:00
adiaholic
d6e25730d0 Saving an instance of ServiceDiscoveryManager in MultiUserChatManager.
Previously, the costly method 'ServiceDiscoveryManager.getInstance()'
was called at multiple instances inside MultiUserChatManager.
In this commit I wish to replace this call by saving a private final
instance of 'ServiceDiscoveryManager' which will is an attempt at
solving SMACK-836.
2019-03-18 13:10:20 +05:30
Florian Schmaus
a619135960 MoodIntegrationTest: Ensure that listener is removed 2019-03-16 22:41:01 +01:00
Florian Schmaus
03fc5eb785 Merge branch '4.3' 2019-03-16 22:15:11 +01:00
Florian Schmaus
9246ea5dca LowLevelRosterIntegrationTest: Use timeout value from superclass 2019-03-16 21:24:04 +01:00
Florian Schmaus
9c4e5d0330 Add Roster.createItem() and Roster.createItemAndRequestSubscription()
and deprecate createEntry().

createEntry() would also send a subscription request which may is
suprising given that you can also create an roster item without having
to send a subscription request out.

This also fixes a bug in
LowLevelRosterIntegrationTest.testPresenceEventListenersOffline()
where createEntry() was used, which would also trigger a presence
subscription request which in turn made the test fail if the
SubscribeListener of ensureSubscribedTo() was not yet set
up. So the test would fail depending on the timing.
2019-03-16 21:23:45 +01:00
Florian Schmaus
c31e93a00f Improve IntegrationTestRosterUtil.ensureSubscribedto()
Improve the names of the arguments and variables. And ensure that all
listeners are removed at the end.
2019-03-16 21:12:05 +01:00
Florian Schmaus
7059b60672 Wait for reader/writer thread termination at the end of shutdown()
This synchronizes the place with what the master branch currently
does.
2019-03-16 11:11:58 +01:00
Florian Schmaus
8e52e80399 Synchronize later in notifyConnectionError(Exception)
especially *after* the sync points have been notified so that a
potential thread currently callin gconnect()/login() throws and leaves
the synchronized section.

This commit is more or less equivalent to
3ded023629 of the 4.3 branch.
2019-03-16 10:43:41 +01:00
Florian Schmaus
3ded023629 Remove 'synchronized' from notifyConnectionError()
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)
2019-03-16 10:42:36 +01:00
Florian Schmaus
29af92cbd1 Smack 4.3.4-SNAPSHOT 2019-03-15 09:43:09 +01:00
Heckel
3d3d89612f PingManager: Assume server ping successful if stanzas where received
We assume that the connection is good even if there was no ping
result but a stanzas within the ping interval.

In some case we have produced so much load, that the ping result
was not delivered in time because so many other packets were
delivered before.
2019-03-14 12:42:46 +01:00
17 changed files with 174 additions and 57 deletions

7
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,7 @@
Thank you for your contribution!
Before creating a Pull Request, please make sure to
* read https://github.com/igniterealtime/Smack/wiki/Guidelines-for-Smack-Developers-and-Contributors
* run `gradle check` successfully in order to make sure that your code does not break any JUnit tests and is conform to the projects code style.
* (if applicable) mention any Jira issue codes (eg. `SMACK-XXX`) in the *body* of your commit message (not the header), so that Jira automatically links the PR to the issue.
* squash your commits if possible/sensible.

View file

@ -80,6 +80,7 @@ debugger=console
### Where to place the properties file ### Where to place the properties file
The framework will first load the properties file from `~/.config/smack-integration-test/properties` The framework will first load the properties file from `~/.config/smack-integration-test/properties`
Overview of the components Overview of the components
-------------------------- --------------------------

View file

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

View file

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

View file

@ -22,6 +22,7 @@ import java.io.PipedReader;
import java.io.PipedWriter; import java.io.PipedWriter;
import java.io.StringReader; import java.io.StringReader;
import java.io.Writer; import java.io.Writer;
import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -157,8 +158,13 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
if (config.isProxyEnabled()) { if (config.isProxyEnabled()) {
cfgBuilder.setProxy(config.getProxyAddress(), config.getProxyPort()); cfgBuilder.setProxy(config.getProxyAddress(), config.getProxyPort());
} }
cfgBuilder.setCompressionEnabled(config.isCompressionEnabled()); cfgBuilder.setCompressionEnabled(config.isCompressionEnabled());
for (Map.Entry<String, String> h : config.getHttpHeaders().entrySet()) {
cfgBuilder.addHttpHeader(h.getKey(), h.getValue());
}
client = BOSHClient.create(cfgBuilder.build()); client = BOSHClient.create(cfgBuilder.build());
client.addBOSHClientConnListener(new BOSHConnectionListener()); client.addBOSHClientConnListener(new BOSHConnectionListener());

View file

@ -858,9 +858,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
ASYNC_BUT_ORDERED.performAsyncButOrdered(this, () -> { ASYNC_BUT_ORDERED.performAsyncButOrdered(this, () -> {
currentConnectionException = exception; currentConnectionException = exception;
synchronized (AbstractXMPPConnection.this) {
notifyAll();
}
for (StanzaCollector collector : collectors) { for (StanzaCollector collector : collectors) {
collector.notifyConnectionError(exception); collector.notifyConnectionError(exception);
@ -873,10 +870,14 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
// XMPPTCPConnection. Create delegation method? // XMPPTCPConnection. Create delegation method?
// maybeCompressFeaturesReceived.reportGenericFailure(smackWrappedException); // maybeCompressFeaturesReceived.reportGenericFailure(smackWrappedException);
synchronized (AbstractXMPPConnection.this) {
notifyAll();
// Closes the connection temporary. A if the connection supports stream management, then a reconnection is // Closes the connection temporary. A if the connection supports stream management, then a reconnection is
// possible. Note that a connection listener of e.g. XMPPTCPConnection will drop the SM state in // possible. Note that a connection listener of e.g. XMPPTCPConnection will drop the SM state in
// case the Exception is a StreamErrorException. // case the Exception is a StreamErrorException.
instantShutdown(); instantShutdown();
}
Async.go(() -> { Async.go(() -> {
// Notify connection listeners of the error. // Notify connection listeners of the error.

View file

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

View file

@ -152,8 +152,11 @@ public final class MultiUserChatManager extends Manager {
private AutoJoinFailedCallback autoJoinFailedCallback; private AutoJoinFailedCallback autoJoinFailedCallback;
private final ServiceDiscoveryManager serviceDiscoveryManager;
private MultiUserChatManager(XMPPConnection connection) { private MultiUserChatManager(XMPPConnection connection) {
super(connection); super(connection);
serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
// Listens for all messages that include a MUCUser extension and fire the invitation // Listens for all messages that include a MUCUser extension and fire the invitation
// listeners if the message includes an invitation. // listeners if the message includes an invitation.
StanzaListener invitationPacketListener = new StanzaListener() { StanzaListener invitationPacketListener = new StanzaListener() {
@ -277,7 +280,7 @@ public final class MultiUserChatManager extends Manager {
* @throws InterruptedException * @throws InterruptedException
*/ */
public boolean isServiceEnabled(Jid user) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { public boolean isServiceEnabled(Jid user) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
return ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(user, MUCInitialPresence.NAMESPACE); return serviceDiscoveryManager.supportsFeature(user, MUCInitialPresence.NAMESPACE);
} }
/** /**
@ -304,7 +307,7 @@ public final class MultiUserChatManager extends Manager {
public List<EntityBareJid> getJoinedRooms(EntityJid user) throws NoResponseException, XMPPErrorException, public List<EntityBareJid> getJoinedRooms(EntityJid user) throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException { NotConnectedException, InterruptedException {
// Send the disco packet to the user // Send the disco packet to the user
DiscoverItems result = ServiceDiscoveryManager.getInstanceFor(connection()).discoverItems(user, DISCO_NODE); DiscoverItems result = serviceDiscoveryManager.discoverItems(user, DISCO_NODE);
List<DiscoverItems.Item> items = result.getItems(); List<DiscoverItems.Item> items = result.getItems();
List<EntityBareJid> answer = new ArrayList<>(items.size()); List<EntityBareJid> answer = new ArrayList<>(items.size());
// Collect the entityID for each returned item // Collect the entityID for each returned item
@ -331,7 +334,7 @@ public final class MultiUserChatManager extends Manager {
* @throws InterruptedException * @throws InterruptedException
*/ */
public RoomInfo getRoomInfo(EntityBareJid room) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { public RoomInfo getRoomInfo(EntityBareJid room) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
DiscoverInfo info = ServiceDiscoveryManager.getInstanceFor(connection()).discoverInfo(room); DiscoverInfo info = serviceDiscoveryManager.discoverInfo(room);
return new RoomInfo(info); return new RoomInfo(info);
} }
@ -345,8 +348,7 @@ public final class MultiUserChatManager extends Manager {
* @throws InterruptedException * @throws InterruptedException
*/ */
public List<DomainBareJid> getMucServiceDomains() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { public List<DomainBareJid> getMucServiceDomains() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection()); return serviceDiscoveryManager.findServices(MUCInitialPresence.NAMESPACE, false, false);
return sdm.findServices(MUCInitialPresence.NAMESPACE, false, false);
} }
/** /**
@ -379,7 +381,7 @@ public final class MultiUserChatManager extends Manager {
*/ */
public boolean providesMucService(DomainBareJid domainBareJid) throws NoResponseException, public boolean providesMucService(DomainBareJid domainBareJid) throws NoResponseException,
XMPPErrorException, NotConnectedException, InterruptedException { XMPPErrorException, NotConnectedException, InterruptedException {
return ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(domainBareJid, return serviceDiscoveryManager.supportsFeature(domainBareJid,
MUCInitialPresence.NAMESPACE); MUCInitialPresence.NAMESPACE);
} }
@ -402,8 +404,7 @@ public final class MultiUserChatManager extends Manager {
if (!providesMucService(serviceName)) { if (!providesMucService(serviceName)) {
throw new NotAMucServiceException(serviceName); throw new NotAMucServiceException(serviceName);
} }
ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection()); DiscoverItems discoverItems = serviceDiscoveryManager.discoverItems(serviceName);
DiscoverItems discoverItems = discoManager.discoverItems(serviceName);
List<DiscoverItems.Item> items = discoverItems.getItems(); List<DiscoverItems.Item> items = discoverItems.getItems();
Map<EntityBareJid, HostedRoom> answer = new HashMap<>(items.size()); Map<EntityBareJid, HostedRoom> answer = new HashMap<>(items.size());

View file

@ -447,6 +447,19 @@ public final class PingManager extends Manager {
pingFuture.onError(new ExceptionCallback<Exception>() { pingFuture.onError(new ExceptionCallback<Exception>() {
@Override @Override
public void processException(Exception exception) { public void processException(Exception exception) {
long lastStanzaReceived = connection.getLastStanzaReceived();
if (lastStanzaReceived > 0) {
long now = System.currentTimeMillis();
// Delta since the last stanza was received
int deltaInSeconds = (int) ((now - lastStanzaReceived) / 1000);
// If the delta is smaller then the ping interval, we have got an valid stanza in time
// So not error notification needed
if (deltaInSeconds < pingInterval) {
maybeSchedulePingServerTask(deltaInSeconds);
return;
}
}
for (PingFailedListener l : pingFailedListeners) { for (PingFailedListener l : pingFailedListeners) {
l.pingFailed(); l.pingFailed();
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2003-2007 Jive Software, 2016-2017 Florian Schmaus. * Copyright 2003-2007 Jive Software, 2016-2019 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.
@ -629,14 +629,40 @@ public final class Roster extends Manager {
* @throws NotLoggedInException If not logged in. * @throws NotLoggedInException If not logged in.
* @throws NotConnectedException * @throws NotConnectedException
* @throws InterruptedException * @throws InterruptedException
* @deprecated use {@link #createItemAndRequestSubscription(BareJid, String, String[])} instead.
*/ */
// TODO: Remove in Smack 4.5.
@Deprecated
public void createEntry(BareJid user, String name, String[] groups) throws NotLoggedInException, NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { public void createEntry(BareJid user, String name, String[] groups) throws NotLoggedInException, NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
createItemAndRequestSubscription(user, name, groups);
}
/**
* Creates a new roster item. The server will asynchronously update the roster with the subscription status.
* <p>
* There will be no presence subscription request. Consider using
* {@link #createItemAndRequestSubscription(BareJid, String, String[])} if you also want to request a presence
* subscription from the contact.
* </p>
*
* @param jid the XMPP address of the contact (e.g. johndoe@jabber.org)
* @param name the nickname of the user.
* @param groups the list of group names the entry will belong to, or <tt>null</tt> if the the roster entry won't
* belong to a group.
* @throws NoResponseException if there was no response from the server.
* @throws XMPPErrorException if an XMPP exception occurs.
* @throws NotLoggedInException If not logged in.
* @throws NotConnectedException
* @throws InterruptedException
* @since 4.4.0
*/
public void createItem(BareJid jid, String name, String[] groups) throws NotLoggedInException, NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
final XMPPConnection connection = getAuthenticatedConnectionOrThrow(); final XMPPConnection connection = getAuthenticatedConnectionOrThrow();
// Create and send roster entry creation packet. // Create and send roster entry creation packet.
RosterPacket rosterPacket = new RosterPacket(); RosterPacket rosterPacket = new RosterPacket();
rosterPacket.setType(IQ.Type.set); rosterPacket.setType(IQ.Type.set);
RosterPacket.Item item = new RosterPacket.Item(user, name); RosterPacket.Item item = new RosterPacket.Item(jid, name);
if (groups != null) { if (groups != null) {
for (String group : groups) { for (String group : groups) {
if (group != null && group.trim().length() > 0) { if (group != null && group.trim().length() > 0) {
@ -646,8 +672,27 @@ public final class Roster extends Manager {
} }
rosterPacket.addRosterItem(item); rosterPacket.addRosterItem(item);
connection.createStanzaCollectorAndSend(rosterPacket).nextResultOrThrow(); connection.createStanzaCollectorAndSend(rosterPacket).nextResultOrThrow();
}
sendSubscriptionRequest(user); /**
* Creates a new roster entry and presence subscription. The server will asynchronously
* update the roster with the subscription status.
*
* @param jid the XMPP address of the contact (e.g. johndoe@jabber.org)
* @param name the nickname of the user.
* @param groups the list of group names the entry will belong to, or <tt>null</tt> if the
* the roster entry won't belong to a group.
* @throws NoResponseException if there was no response from the server.
* @throws XMPPErrorException if an XMPP exception occurs.
* @throws NotLoggedInException If not logged in.
* @throws NotConnectedException
* @throws InterruptedException
* @since 4.4.0
*/
public void createItemAndRequestSubscription(BareJid jid, String name, String[] groups) throws NotLoggedInException, NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
createItem(jid, name, groups);
sendSubscriptionRequest(jid);
} }
/** /**
@ -668,7 +713,7 @@ public final class Roster extends Manager {
*/ */
public void preApproveAndCreateEntry(BareJid user, String name, String[] groups) throws NotLoggedInException, NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, FeatureNotSupportedException { public void preApproveAndCreateEntry(BareJid user, String name, String[] groups) throws NotLoggedInException, NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, FeatureNotSupportedException {
preApprove(user); preApprove(user);
createEntry(user, name, groups); createItemAndRequestSubscription(user, name, groups);
} }
/** /**

View file

@ -164,7 +164,7 @@ public class RosterTest extends InitSmackIm {
} }
}; };
serverSimulator.start(); serverSimulator.start();
roster.createEntry(contactJID, contactName, contactGroup); roster.createItemAndRequestSubscription(contactJID, contactName, contactGroup);
serverSimulator.join(); serverSimulator.join();
// Check if an error occurred within the simulator // Check if an error occurred within the simulator
@ -430,7 +430,7 @@ public class RosterTest extends InitSmackIm {
} }
}; };
serverSimulator.start(); serverSimulator.start();
roster.createEntry(contactJID, contactName, contactGroup); roster.createItemAndRequestSubscription(contactJID, contactName, contactGroup);
serverSimulator.join(); serverSimulator.join();
// Check if an error occurred within the simulator // Check if an error occurred within the simulator

View file

@ -299,10 +299,32 @@ public class XmppConnectionStressTest {
private ErrorsWhileSendingOrReceivingException(Map<XMPPConnection, Exception> sendExceptions, private ErrorsWhileSendingOrReceivingException(Map<XMPPConnection, Exception> sendExceptions,
Map<XMPPConnection, Exception> receiveExceptions) { Map<XMPPConnection, Exception> receiveExceptions) {
super("Exceptions while sending and/or receiving"); super(createMessageFrom(sendExceptions, receiveExceptions));
this.sendExceptions = sendExceptions; this.sendExceptions = sendExceptions;
this.receiveExceptions = receiveExceptions; this.receiveExceptions = receiveExceptions;
} }
private static String createMessageFrom(Map<XMPPConnection, Exception> sendExceptions,
Map<XMPPConnection, Exception> receiveExceptions) {
StringBuilder sb = new StringBuilder(1024);
sb.append("Exceptions while sending and/or receiving.");
if (!sendExceptions.isEmpty()) {
sb.append(" Send exxceptions: ");
for (Map.Entry<XMPPConnection, Exception> entry : sendExceptions.entrySet()) {
sb.append(entry.getKey()).append(": ").append(entry.getValue()).append(';');
}
}
if (!receiveExceptions.isEmpty()) {
sb.append(" Receive exceptions: ");
for (Map.Entry<XMPPConnection, Exception> entry : receiveExceptions.entrySet()) {
sb.append(entry.getKey()).append(": ").append(entry.getValue());
}
}
return sb.toString();
}
} }
} }
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2015-2018 Florian Schmaus * Copyright 2015-2019 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,11 +25,13 @@ import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.roster.AbstractPresenceEventListener; import org.jivesoftware.smack.roster.AbstractPresenceEventListener;
import org.jivesoftware.smack.roster.PresenceEventListener;
import org.jivesoftware.smack.roster.Roster; import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.roster.RosterEntry; import org.jivesoftware.smack.roster.RosterEntry;
import org.jivesoftware.smack.roster.SubscribeListener; import org.jivesoftware.smack.roster.SubscribeListener;
import org.jxmpp.jid.BareJid; import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.Jid; import org.jxmpp.jid.Jid;
public class IntegrationTestRosterUtil { public class IntegrationTestRosterUtil {
@ -39,41 +41,47 @@ public class IntegrationTestRosterUtil {
ensureSubscribedTo(conTwo, conOne, timeout); ensureSubscribedTo(conTwo, conOne, timeout);
} }
public static void ensureSubscribedTo(final XMPPConnection conOne, final XMPPConnection conTwo, long timeout) throws TimeoutException, Exception { public static void ensureSubscribedTo(final XMPPConnection presenceRequestReceiverConnection, final XMPPConnection presenceRequestingConnection, long timeout) throws TimeoutException, Exception {
Roster rosterOne = Roster.getInstanceFor(conOne); final Roster presenceRequestReceiverRoster = Roster.getInstanceFor(presenceRequestReceiverConnection);
Roster rosterTwo = Roster.getInstanceFor(conTwo); final Roster presenceRequestingRoster = Roster.getInstanceFor(presenceRequestingConnection);
if (rosterOne.isSubscribedToMyPresence(conTwo.getUser())) { final EntityFullJid presenceRequestReceiverAddress = presenceRequestReceiverConnection.getUser();
final EntityFullJid presenceRequestingAddress = presenceRequestingConnection.getUser();
if (presenceRequestReceiverRoster.isSubscribedToMyPresence(presenceRequestingAddress)) {
return; return;
} }
final SubscribeListener subscribeListener = new SubscribeListener() { final SubscribeListener subscribeListener = new SubscribeListener() {
@Override @Override
public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) { public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) {
if (from.equals(conTwo.getUser().asBareJid())) { if (from.equals(presenceRequestingConnection.getUser().asBareJid())) {
return SubscribeAnswer.Approve; return SubscribeAnswer.Approve;
} }
return SubscribeAnswer.Deny; return SubscribeAnswer.Deny;
} }
}; };
rosterOne.addSubscribeListener(subscribeListener); presenceRequestReceiverRoster.addSubscribeListener(subscribeListener);
final SimpleResultSyncPoint syncPoint = new SimpleResultSyncPoint(); final SimpleResultSyncPoint syncPoint = new SimpleResultSyncPoint();
rosterTwo.addPresenceEventListener(new AbstractPresenceEventListener() { final PresenceEventListener presenceEventListener = new AbstractPresenceEventListener() {
@Override @Override
public void presenceSubscribed(BareJid address, Presence subscribedPresence) { public void presenceSubscribed(BareJid address, Presence subscribedPresence) {
if (!address.equals(conOne.getUser().asBareJid())) { if (!address.equals(presenceRequestReceiverAddress.asBareJid())) {
return; return;
} }
syncPoint.signal(); syncPoint.signal();
} }
}); };
rosterTwo.sendSubscriptionRequest(conOne.getUser().asBareJid()); presenceRequestingRoster.addPresenceEventListener(presenceEventListener);
try { try {
presenceRequestingRoster.sendSubscriptionRequest(presenceRequestReceiverAddress.asBareJid());
syncPoint.waitForResult(timeout); syncPoint.waitForResult(timeout);
} finally { } finally {
rosterOne.removeSubscribeListener(subscribeListener); presenceRequestReceiverRoster.removeSubscribeListener(subscribeListener);
presenceRequestingRoster.removePresenceEventListener(presenceEventListener);
} }
} }

View file

@ -42,12 +42,9 @@ public class LowLevelRosterIntegrationTest extends AbstractSmackLowLevelIntegrat
final Roster rosterOne = Roster.getInstanceFor(conOne); final Roster rosterOne = Roster.getInstanceFor(conOne);
final Roster rosterTwo = Roster.getInstanceFor(conTwo); final Roster rosterTwo = Roster.getInstanceFor(conTwo);
// TODO create Roster.createEntry() with boolean flag for subscribe or not. rosterOne.createItem(conTwo.getUser().asBareJid(), "Con Two", null);
rosterOne.createEntry(conTwo.getUser().asBareJid(), "Con Two", null); rosterTwo.createItem(conOne.getUser().asBareJid(), "Con One", null);
rosterTwo.createEntry(conOne.getUser().asBareJid(), "Con One", null);
// TODO Change timeout form '5000' to something configurable.
final long timeout = 5000;
IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout); IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout);
final SimpleResultSyncPoint offlineTriggered = new SimpleResultSyncPoint(); final SimpleResultSyncPoint offlineTriggered = new SimpleResultSyncPoint();

View file

@ -99,7 +99,7 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest {
}); });
try { try {
rosterOne.createEntry(conTwo.getUser().asBareJid(), conTwosRosterName, null); rosterOne.createItemAndRequestSubscription(conTwo.getUser().asBareJid(), conTwosRosterName, null);
assertTrue(addedAndSubscribed.waitForResult(2 * connection.getReplyTimeout())); assertTrue(addedAndSubscribed.waitForResult(2 * connection.getReplyTimeout()));
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2018 Paul Schaub. * Copyright 2018 Paul Schaub, 2019 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.
@ -18,9 +18,6 @@ package org.jivesoftware.smackx.mood;
import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.mood.element.MoodElement;
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTest;
@ -28,7 +25,6 @@ import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil; import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil;
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.jxmpp.jid.BareJid;
public class MoodIntegrationTest extends AbstractSmackIntegrationTest { public class MoodIntegrationTest extends AbstractSmackIntegrationTest {
@ -47,18 +43,20 @@ public class MoodIntegrationTest extends AbstractSmackIntegrationTest {
final SimpleResultSyncPoint moodReceived = new SimpleResultSyncPoint(); final SimpleResultSyncPoint moodReceived = new SimpleResultSyncPoint();
mm2.addMoodListener(new MoodListener() { final MoodListener moodListener = (jid, message, moodElement) -> {
@Override
public void onMoodUpdated(BareJid jid, Message message, MoodElement moodElement) {
if (moodElement.getMood() == Mood.satisfied) { if (moodElement.getMood() == Mood.satisfied) {
moodReceived.signal(); moodReceived.signal();
} }
} };
}); mm2.addMoodListener(moodListener);
try {
mm1.setMood(Mood.satisfied); mm1.setMood(Mood.satisfied);
moodReceived.waitForResult(timeout); moodReceived.waitForResult(timeout);
} finally {
mm2.removeMoodListener(moodListener);
}
} }
@AfterClass @AfterClass

View file

@ -609,6 +609,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
proxyInfo.getProxySocketConnection().connect(socket, host, port, timeout); proxyInfo.getProxySocketConnection().connect(socket, host, port, timeout);
} catch (IOException e) { } catch (IOException e) {
hostAddress.setException(e); hostAddress.setException(e);
failedAddresses.add(hostAddress);
continue; continue;
} }
LOGGER.finer("Established TCP connection to " + hostAndPort); LOGGER.finer("Established TCP connection to " + hostAndPort);