Refactor FileTransfer(Manager|Negotiator)

to use WeakHashMaps and extend Manager.

SMACK-579
This commit is contained in:
Florian Schmaus 2014-07-02 00:17:04 +02:00
parent fdaf7940fb
commit 09425609af
8 changed files with 66 additions and 133 deletions

View File

@ -33,9 +33,8 @@ to enable the user to easily send a file.
<b>Usage</b><p> <b>Usage</b><p>
In order to send a file you must first construct an instance of the <b><i>FileTransferManager</i></b> In order to send a file you must first construct an instance of the <b><i>FileTransferManager</i></b> class.
class. This class has one constructor with one parameter which is your XMPPConnection. In order to instantiate the manager you should call <i>FileTransferManager.getInstanceFor(connection)</i>
In order to instantiate the manager you should call <i>new FileTransferManager(connection)</i>
<p>Once you have your <b><i>FileTransferManager</i></b> you will need to create an outgoing <p>Once you have your <b><i>FileTransferManager</i></b> you will need to create an outgoing
file transfer to send a file. The method to use on the <b><i>FileTransferManager</i></b> file transfer to send a file. The method to use on the <b><i>FileTransferManager</i></b>
@ -62,7 +61,7 @@ In this example we can see how to send a file: <br>
<blockquote> <blockquote>
<pre> <pre>
<font color="#3f7f5f">// Create the file transfer manager</font> <font color="#3f7f5f">// Create the file transfer manager</font>
FileTransferManager manager = new FileTransferManager(connection); FileTransferManager manager = FileTransferManager.getInstanceFor(connection);
<font color="#3f7f5f">// Create the outgoing file transfer</font> <font color="#3f7f5f">// Create the outgoing file transfer</font>
OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer(<font color="#0000FF">"romeo@montague.net"</font>); OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer(<font color="#0000FF">"romeo@montague.net"</font>);
@ -85,9 +84,8 @@ manager.</p>
<b>Usage</b><p> <b>Usage</b><p>
In order to recieve a file you must first construct an instance of the <b><i>FileTransferManager</i></b> In order to recieve a file you must first construct an instance of the <b><i>FileTransferManager</i></b> class.
class. This class has one constructor with one parameter which is your XMPPConnection. In order to instantiate the manager you should call <i>FileTransferManager.getInstanceFor(connection)</i>
In order to instantiate the manager you should call <i>new FileTransferManager(connection)</i>
<p>Once you have your <b><i>FileTransferManager</i></b> you will need to register a listener <p>Once you have your <b><i>FileTransferManager</i></b> you will need to register a listener
with it. The FileTransferListner interface has one method, <b>fileTransferRequest(request)</b>. with it. The FileTransferListner interface has one method, <b>fileTransferRequest(request)</b>.
@ -114,7 +112,7 @@ consult the Javadoc for more information.
In this example we can see how to approve or reject a file transfer request: <br> In this example we can see how to approve or reject a file transfer request: <br>
<blockquote> <blockquote>
<pre> <font color="#3f7f5f">// Create the file transfer manager</font> <pre> <font color="#3f7f5f">// Create the file transfer manager</font>
final FileTransferManager manager = new FileTransferManager(connection); FileTransferManager manager = FileTransferManager.getInstanceFor(connection);
<font color="#3f7f5f">// Create the listener</font> <font color="#3f7f5f">// Create the listener</font>
manager.addFileTransferListener(new FileTransferListener() { manager.addFileTransferListener(new FileTransferListener() {

View File

@ -23,6 +23,9 @@ public abstract class Manager {
final WeakReference<XMPPConnection> weakConnection; final WeakReference<XMPPConnection> weakConnection;
public Manager(XMPPConnection connection) { public Manager(XMPPConnection connection) {
if (connection == null) {
throw new IllegalArgumentException("XMPPConnection must not be null");
}
weakConnection = new WeakReference<XMPPConnection>(connection); weakConnection = new WeakReference<XMPPConnection>(connection);
} }

View File

@ -161,9 +161,6 @@ public class FaultTolerantNegotiator extends StreamNegotiator {
return namespaces; return namespaces;
} }
public void cleanup() {
}
private class NegotiatorService implements Callable<InputStream> { private class NegotiatorService implements Callable<InputStream> {
private PacketCollector collector; private PacketCollector collector;

View File

@ -16,6 +16,7 @@
*/ */
package org.jivesoftware.smackx.filetransfer; package org.jivesoftware.smackx.filetransfer;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
@ -28,8 +29,10 @@ import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smackx.si.packet.StreamInitiation; import org.jivesoftware.smackx.si.packet.StreamInitiation;
import org.jxmpp.util.XmppStringUtils; import org.jxmpp.util.XmppStringUtils;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
/** /**
* The file transfer manager class handles the sending and recieving of files. * The file transfer manager class handles the sending and recieving of files.
@ -43,13 +46,22 @@ import java.util.List;
* @author Alexander Wenckus * @author Alexander Wenckus
* *
*/ */
public class FileTransferManager { public class FileTransferManager extends Manager {
private static final Map<XMPPConnection, FileTransferManager> INSTANCES = new WeakHashMap<XMPPConnection, FileTransferManager>();
public static synchronized FileTransferManager getInstanceFor(XMPPConnection connection) {
FileTransferManager fileTransferManager = INSTANCES.get(connection);
if (fileTransferManager == null) {
fileTransferManager = new FileTransferManager(connection);
INSTANCES.put(connection, fileTransferManager);
}
return fileTransferManager;
}
private final FileTransferNegotiator fileTransferNegotiator; private final FileTransferNegotiator fileTransferNegotiator;
private List<FileTransferListener> listeners; private final List<FileTransferListener> listeners = new CopyOnWriteArrayList<FileTransferListener>();
private XMPPConnection connection;
/** /**
* Creates a file transfer manager to initiate and receive file transfers. * Creates a file transfer manager to initiate and receive file transfers.
@ -57,10 +69,19 @@ public class FileTransferManager {
* @param connection * @param connection
* The XMPPConnection that the file transfers will use. * The XMPPConnection that the file transfers will use.
*/ */
public FileTransferManager(XMPPConnection connection) { private FileTransferManager(XMPPConnection connection) {
this.connection = connection; super(connection);
this.fileTransferNegotiator = FileTransferNegotiator this.fileTransferNegotiator = FileTransferNegotiator
.getInstanceFor(connection); .getInstanceFor(connection);
connection.addPacketListener(new PacketListener() {
public void processPacket(Packet packet) {
StreamInitiation si = (StreamInitiation) packet;
FileTransferRequest request = new FileTransferRequest(FileTransferManager.this, si);
for (FileTransferListener listener : listeners) {
listener.fileTransferRequest(request);
}
}
}, new AndFilter(new PacketTypeFilter(StreamInitiation.class), IQTypeFilter.SET));
} }
/** /**
@ -73,35 +94,7 @@ public class FileTransferManager {
* @see FileTransferListener * @see FileTransferListener
*/ */
public void addFileTransferListener(final FileTransferListener li) { public void addFileTransferListener(final FileTransferListener li) {
if (listeners == null) { listeners.add(li);
initListeners();
}
synchronized (this.listeners) {
listeners.add(li);
}
}
private void initListeners() {
listeners = new ArrayList<FileTransferListener>();
connection.addPacketListener(new PacketListener() {
public void processPacket(Packet packet) {
fireNewRequest((StreamInitiation) packet);
}
}, new AndFilter(new PacketTypeFilter(StreamInitiation.class),
IQTypeFilter.SET));
}
protected void fireNewRequest(StreamInitiation initiation) {
FileTransferListener[] listeners = null;
synchronized (this.listeners) {
listeners = new FileTransferListener[this.listeners.size()];
this.listeners.toArray(listeners);
}
FileTransferRequest request = new FileTransferRequest(this, initiation);
for (int i = 0; i < listeners.length; i++) {
listeners[i].fileTransferRequest(request);
}
} }
/** /**
@ -112,12 +105,7 @@ public class FileTransferManager {
* @see FileTransferListener * @see FileTransferListener
*/ */
public void removeFileTransferListener(final FileTransferListener li) { public void removeFileTransferListener(final FileTransferListener li) {
if (listeners == null) { listeners.remove(li);
return;
}
synchronized (this.listeners) {
listeners.remove(li);
}
} }
/** /**
@ -140,7 +128,7 @@ public class FileTransferManager {
throw new IllegalArgumentException("The provided user id was not a full JID (i.e. with resource part)"); throw new IllegalArgumentException("The provided user id was not a full JID (i.e. with resource part)");
} }
return new OutgoingFileTransfer(connection.getUser(), userID, return new OutgoingFileTransfer(connection().getUser(), userID,
fileTransferNegotiator.getNextStreamID(), fileTransferNegotiator.getNextStreamID(),
fileTransferNegotiator); fileTransferNegotiator);
} }
@ -175,6 +163,6 @@ public class FileTransferManager {
initiation.getPacketID(), initiation.getFrom(), initiation initiation.getPacketID(), initiation.getFrom(), initiation
.getTo(), IQ.Type.error); .getTo(), IQ.Type.error);
rejection.setError(new XMPPError(XMPPError.Condition.no_acceptable)); rejection.setError(new XMPPError(XMPPError.Condition.no_acceptable));
connection.sendPacket(rejection); connection().sendPacket(rejection);
} }
} }

View File

@ -24,9 +24,9 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ConcurrentHashMap; import java.util.WeakHashMap;
import org.jivesoftware.smack.AbstractConnectionListener; import org.jivesoftware.smack.Manager;
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.PacketCollector; import org.jivesoftware.smack.PacketCollector;
@ -50,16 +50,13 @@ import org.jivesoftware.smackx.xdata.packet.DataForm;
* @author Alexander Wenckus * @author Alexander Wenckus
* @see <a href="http://xmpp.org/extensions/xep-0096.html">XEP-0096: SI File Transfer</a> * @see <a href="http://xmpp.org/extensions/xep-0096.html">XEP-0096: SI File Transfer</a>
*/ */
public class FileTransferNegotiator { public class FileTransferNegotiator extends Manager {
// Static public static final String SI_NAMESPACE = "http://jabber.org/protocol/si";
public static final String SI_PROFILE_FILE_TRANSFER_NAMESPACE = "http://jabber.org/protocol/si/profile/file-transfer";
private static final String[] NAMESPACE = { SI_NAMESPACE, SI_PROFILE_FILE_TRANSFER_NAMESPACE };
private static final String[] NAMESPACE = { private static final Map<XMPPConnection, FileTransferNegotiator> INSTANCES = new WeakHashMap<XMPPConnection, FileTransferNegotiator>();
"http://jabber.org/protocol/si/profile/file-transfer",
"http://jabber.org/protocol/si"};
private static final Map<XMPPConnection, FileTransferNegotiator> transferObject =
new ConcurrentHashMap<XMPPConnection, FileTransferNegotiator>();
private static final String STREAM_INIT_PREFIX = "jsi_"; private static final String STREAM_INIT_PREFIX = "jsi_";
@ -80,27 +77,16 @@ public class FileTransferNegotiator {
* service is automatically enabled. * service is automatically enabled.
* *
* @param connection The connection for which the transfer manager is desired * @param connection The connection for which the transfer manager is desired
* @return The IMFileTransferManager * @return The FileTransferNegotiator
*/ */
public static FileTransferNegotiator getInstanceFor( public static synchronized FileTransferNegotiator getInstanceFor(
final XMPPConnection connection) { final XMPPConnection connection) {
if (connection == null) { FileTransferNegotiator fileTransferNegotiator = INSTANCES.get(connection);
throw new IllegalArgumentException("XMPPConnection cannot be null"); if (fileTransferNegotiator == null) {
} fileTransferNegotiator = new FileTransferNegotiator(connection);
if (!connection.isConnected()) { INSTANCES.put(connection, fileTransferNegotiator);
return null;
}
if (transferObject.containsKey(connection)) {
return transferObject.get(connection);
}
else {
FileTransferNegotiator transfer = new FileTransferNegotiator(
connection);
setServiceEnabled(connection, true);
transferObject.put(connection, transfer);
return transfer;
} }
return fileTransferNegotiator;
} }
/** /**
@ -110,7 +96,7 @@ public class FileTransferNegotiator {
* @param connection The connection on which to enable or disable the services. * @param connection The connection on which to enable or disable the services.
* @param isEnabled True to enable, false to disable. * @param isEnabled True to enable, false to disable.
*/ */
public static void setServiceEnabled(final XMPPConnection connection, private static void setServiceEnabled(final XMPPConnection connection,
final boolean isEnabled) { final boolean isEnabled) {
ServiceDiscoveryManager manager = ServiceDiscoveryManager ServiceDiscoveryManager manager = ServiceDiscoveryManager
.getInstanceFor(connection); .getInstanceFor(connection);
@ -124,14 +110,11 @@ public class FileTransferNegotiator {
for (String namespace : namespaces) { for (String namespace : namespaces) {
if (isEnabled) { if (isEnabled) {
if (!manager.includesFeature(namespace)) { manager.addFeature(namespace);
manager.addFeature(namespace);
}
} else { } else {
manager.removeFeature(namespace); manager.removeFeature(namespace);
} }
} }
} }
/** /**
@ -200,38 +183,16 @@ public class FileTransferNegotiator {
// non-static // non-static
private final XMPPConnection connection;
private final StreamNegotiator byteStreamTransferManager; private final StreamNegotiator byteStreamTransferManager;
private final StreamNegotiator inbandTransferManager; private final StreamNegotiator inbandTransferManager;
private FileTransferNegotiator(final XMPPConnection connection) { private FileTransferNegotiator(final XMPPConnection connection) {
configureConnection(connection); super(connection);
this.connection = connection;
byteStreamTransferManager = new Socks5TransferNegotiator(connection); byteStreamTransferManager = new Socks5TransferNegotiator(connection);
inbandTransferManager = new IBBTransferNegotiator(connection); inbandTransferManager = new IBBTransferNegotiator(connection);
}
private void configureConnection(final XMPPConnection connection) { setServiceEnabled(connection, true);
connection.addConnectionListener(new AbstractConnectionListener() {
@Override
public void connectionClosed() {
cleanup(connection);
}
@Override
public void connectionClosedOnError(Exception e) {
cleanup(connection);
}
});
}
private void cleanup(final XMPPConnection connection) {
if (transferObject.remove(connection) != null) {
inbandTransferManager.cleanup();
}
} }
/** /**
@ -255,7 +216,7 @@ public class FileTransferNegotiator {
IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(), IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(),
IQ.Type.error); IQ.Type.error);
iqPacket.setError(error); iqPacket.setError(error);
connection.sendPacket(iqPacket); connection().sendPacket(iqPacket);
throw new XMPPErrorException(errorMessage, error); throw new XMPPErrorException(errorMessage, error);
} }
@ -269,7 +230,7 @@ public class FileTransferNegotiator {
IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(), IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(),
IQ.Type.error); IQ.Type.error);
iqPacket.setError(e.getXMPPError()); iqPacket.setError(e.getXMPPError());
connection.sendPacket(iqPacket); connection().sendPacket(iqPacket);
throw e; throw e;
} }
@ -308,9 +269,8 @@ public class FileTransferNegotiator {
throw new XMPPErrorException(error); throw new XMPPErrorException(error);
} }
//if (isByteStream && isIBB && field.getType().equals(FormField.TYPE_LIST_MULTI)) {
if (isByteStream && isIBB) { if (isByteStream && isIBB) {
return new FaultTolerantNegotiator(connection, return new FaultTolerantNegotiator(connection(),
byteStreamTransferManager, byteStreamTransferManager,
inbandTransferManager); inbandTransferManager);
} }
@ -333,7 +293,7 @@ public class FileTransferNegotiator {
IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(), IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(),
IQ.Type.error); IQ.Type.error);
iqPacket.setError(error); iqPacket.setError(error);
connection.sendPacket(iqPacket); connection().sendPacket(iqPacket);
} }
/** /**
@ -394,11 +354,11 @@ public class FileTransferNegotiator {
si.setFeatureNegotiationForm(createDefaultInitiationForm()); si.setFeatureNegotiationForm(createDefaultInitiationForm());
si.setFrom(connection.getUser()); si.setFrom(connection().getUser());
si.setTo(userID); si.setTo(userID);
si.setType(IQ.Type.set); si.setType(IQ.Type.set);
PacketCollector collector = connection.createPacketCollectorAndSend(si); PacketCollector collector = connection().createPacketCollectorAndSend(si);
Packet siResponse = collector.nextResult(responseTimeout); Packet siResponse = collector.nextResult(responseTimeout);
collector.cancel(); collector.cancel();
@ -439,7 +399,7 @@ public class FileTransferNegotiator {
} }
if (isByteStream && isIBB) { if (isByteStream && isIBB) {
return new FaultTolerantNegotiator(connection, return new FaultTolerantNegotiator(connection(),
byteStreamTransferManager, inbandTransferManager); byteStreamTransferManager, inbandTransferManager);
} }
else if (isByteStream) { else if (isByteStream) {

View File

@ -106,9 +106,6 @@ public class IBBTransferNegotiator extends StreamNegotiator {
return session.getInputStream(); return session.getInputStream();
} }
public void cleanup() {
}
/** /**
* This PacketFilter accepts an incoming In-Band Bytestream open request * This PacketFilter accepts an incoming In-Band Bytestream open request
* with a specified session ID. * with a specified session ID.

View File

@ -122,11 +122,6 @@ public class Socks5TransferNegotiator extends StreamNegotiator {
} }
} }
@Override
public void cleanup() {
/* do nothing */
}
/** /**
* This PacketFilter accepts an incoming SOCKS5 Bytestream request with a specified session ID. * This PacketFilter accepts an incoming SOCKS5 Bytestream request with a specified session ID.
*/ */

View File

@ -157,9 +157,4 @@ public abstract class StreamNegotiator {
*/ */
public abstract String[] getNamespaces(); public abstract String[] getNamespaces();
/**
* Cleanup any and all resources associated with this negotiator.
*/
public abstract void cleanup();
} }