Remove ConnectionListener from Socks5BytestreamManager

and use weak references.

Disabling the Socks5Manager every time the connection is terminated, and
re-enabling it when it got connected again causes unwanted side
effects. Like adding a new feature to the ServiceDiscoveryManager causes
an update of the entity's capabilities, which then triggers a new outgoing
presence (announcing the new caps version).

SMACK-671
This commit is contained in:
Florian Schmaus 2015-05-24 17:18:48 +02:00
parent 385798f9ba
commit 35317a19bd
2 changed files with 26 additions and 42 deletions

View File

@ -28,7 +28,7 @@ import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeoutException;
import org.jivesoftware.smack.AbstractConnectionClosedListener;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.FeatureNotSupportedException;
@ -86,7 +86,7 @@ import org.jivesoftware.smackx.filetransfer.FileTransferManager;
*
* @author Henning Staib
*/
public final class Socks5BytestreamManager implements BytestreamManager {
public final class Socks5BytestreamManager extends Manager implements BytestreamManager {
/*
* create a new Socks5BytestreamManager and register a shutdown listener on every established
@ -98,22 +98,6 @@ public final class Socks5BytestreamManager implements BytestreamManager {
public void connectionCreated(final XMPPConnection connection) {
// create the manager for this connection
Socks5BytestreamManager.getBytestreamManager(connection);
// register shutdown listener
connection.addConnectionListener(new AbstractConnectionClosedListener() {
@Override
public void connectionTerminated() {
Socks5BytestreamManager.getBytestreamManager(connection).disableService();
}
@Override
public void reconnectionSuccessful() {
// re-create the manager for this connection
Socks5BytestreamManager.getBytestreamManager(connection);
}
});
}
});
@ -128,9 +112,6 @@ public final class Socks5BytestreamManager implements BytestreamManager {
/* stores one Socks5BytestreamManager for each XMPP connection */
private final static Map<XMPPConnection, Socks5BytestreamManager> managers = new HashMap<XMPPConnection, Socks5BytestreamManager>();
/* XMPP connection */
private final XMPPConnection connection;
/*
* assigns a user to a listener that is informed if a bytestream request for this user is
* received
@ -185,7 +166,6 @@ public final class Socks5BytestreamManager implements BytestreamManager {
if (manager == null) {
manager = new Socks5BytestreamManager(connection);
managers.put(connection, manager);
manager.activate();
}
return manager;
}
@ -196,8 +176,9 @@ public final class Socks5BytestreamManager implements BytestreamManager {
* @param connection the XMPP connection
*/
private Socks5BytestreamManager(XMPPConnection connection) {
this.connection = connection;
super(connection);
this.initiationListener = new InitiationListener(this);
activate();
}
/**
@ -282,7 +263,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
* Using the file transfer API will automatically re-enable the SOCKS5 Bytestream feature.
*/
public synchronized void disableService() {
XMPPConnection connection = connection();
// remove initiation packet listener
connection.unregisterIQRequestHandler(initiationListener);
@ -299,7 +280,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
this.ignoredBytestreamRequests.clear();
// remove manager from static managers map
managers.remove(this.connection);
managers.remove(connection);
// shutdown local SOCKS5 proxy if there are no more managers for other connections
if (managers.size() == 0) {
@ -307,7 +288,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
}
// remove feature from service discovery
ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(this.connection);
ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
// check if service discovery is not already disposed by connection shutdown
if (serviceDiscoveryManager != null) {
@ -423,7 +404,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
*/
public Socks5BytestreamSession establishSession(String targetJID, String sessionID)
throws IOException, InterruptedException, NoResponseException, SmackException, XMPPException{
XMPPConnection connection = connection();
XMPPErrorException discoveryException = null;
// check if target supports SOCKS5 Bytestream
if (!supportsSocks5(targetJID)) {
@ -452,7 +433,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
}
// compute digest
String digest = Socks5Utils.createDigest(sessionID, this.connection.getUser(), targetJID);
String digest = Socks5Utils.createDigest(sessionID, connection.getUser(), targetJID);
// prioritize last working SOCKS5 proxy if exists
if (this.proxyPrioritizationEnabled && this.lastWorkingProxy != null) {
@ -493,7 +474,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
// build SOCKS5 client
Socks5Client socks5Client = new Socks5ClientForInitiator(usedStreamHost, digest,
this.connection, sessionID, targetJID);
connection, sessionID, targetJID);
// establish connection to proxy
Socket socket = socks5Client.getSocket(getProxyConnectionTimeout());
@ -503,7 +484,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
// negotiation successful, return the output stream
return new Socks5BytestreamSession(socket, usedStreamHost.getJID().equals(
this.connection.getUser()));
connection.getUser()));
}
catch (TimeoutException e) {
@ -529,7 +510,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
* @throws NotConnectedException
*/
private boolean supportsSocks5(String targetJID) throws NoResponseException, XMPPErrorException, NotConnectedException {
return ServiceDiscoveryManager.getInstanceFor(connection).supportsFeature(targetJID, Bytestream.NAMESPACE);
return ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(targetJID, Bytestream.NAMESPACE);
}
/**
@ -542,12 +523,13 @@ public final class Socks5BytestreamManager implements BytestreamManager {
* @throws NotConnectedException
*/
private List<String> determineProxies() throws NoResponseException, XMPPErrorException, NotConnectedException {
ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(this.connection);
XMPPConnection connection = connection();
ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
List<String> proxies = new ArrayList<String>();
// get all items from XMPP server
DiscoverItems discoverItems = serviceDiscoveryManager.discoverItems(this.connection.getServiceName());
DiscoverItems discoverItems = serviceDiscoveryManager.discoverItems(connection.getServiceName());
// query all items if they are SOCKS5 proxies
for (Item item : discoverItems.getItems()) {
@ -590,6 +572,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
* @return a list of stream hosts containing the IP address an the port
*/
private List<StreamHost> determineStreamHostInfos(List<String> proxies) {
XMPPConnection connection = connection();
List<StreamHost> streamHosts = new ArrayList<StreamHost>();
// add local proxy on first position if exists
@ -636,7 +619,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
* is not running
*/
private List<StreamHost> getLocalStreamHost() {
XMPPConnection connection = connection();
// get local proxy singleton
Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy();
@ -704,7 +687,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
protected void replyRejectPacket(IQ packet) throws NotConnectedException {
XMPPError xmppError = new XMPPError(XMPPError.Condition.not_acceptable);
IQ errorIQ = IQ.createErrorResponse(packet, xmppError);
this.connection.sendStanza(errorIQ);
connection().sendStanza(errorIQ);
}
/**
@ -713,7 +696,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
*/
private void activate() {
// register bytestream initiation packet listener
connection.registerIQRequestHandler(initiationListener);
connection().registerIQRequestHandler(initiationListener);
// enable SOCKS5 feature
enableService();
@ -723,7 +706,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
* Adds the SOCKS5 Bytestream feature to the service discovery.
*/
private void enableService() {
ServiceDiscoveryManager manager = ServiceDiscoveryManager.getInstanceFor(this.connection);
ServiceDiscoveryManager manager = ServiceDiscoveryManager.getInstanceFor(connection());
manager.addFeature(Bytestream.NAMESPACE);
}
@ -745,7 +728,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
* @return the XMPP connection
*/
protected XMPPConnection getConnection() {
return this.connection;
return connection();
}
/**

View File

@ -17,6 +17,7 @@
package org.jivesoftware.smackx.bytestreams.socks5;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.Socket;
import java.util.concurrent.TimeoutException;
@ -41,7 +42,7 @@ import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;
class Socks5ClientForInitiator extends Socks5Client {
/* the XMPP connection used to communicate with the SOCKS5 proxy */
private XMPPConnection connection;
private WeakReference<XMPPConnection> connection;
/* the session ID used to activate SOCKS5 stream */
private String sessionID;
@ -61,7 +62,7 @@ class Socks5ClientForInitiator extends Socks5Client {
public Socks5ClientForInitiator(StreamHost streamHost, String digest, XMPPConnection connection,
String sessionID, String target) {
super(streamHost, digest);
this.connection = connection;
this.connection = new WeakReference<>(connection);
this.sessionID = sessionID;
this.target = target;
}
@ -71,7 +72,7 @@ class Socks5ClientForInitiator extends Socks5Client {
Socket socket = null;
// check if stream host is the local SOCKS5 proxy
if (this.streamHost.getJID().equals(this.connection.getUser())) {
if (this.streamHost.getJID().equals(this.connection.get().getUser())) {
Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy();
socket = socks5Server.getSocket(this.digest);
if (socket == null) {
@ -109,7 +110,7 @@ class Socks5ClientForInitiator extends Socks5Client {
private void activate() throws NoResponseException, XMPPErrorException, NotConnectedException {
Bytestream activate = createStreamHostActivation();
// if activation fails #nextResultOrThrow() throws an exception
connection.createPacketCollectorAndSend(activate).nextResultOrThrow();
connection.get().createPacketCollectorAndSend(activate).nextResultOrThrow();
}
/**