1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-11-27 14:32:06 +01:00

IBB sending works using worker thread

This commit is contained in:
vanitasvitae 2017-06-22 14:47:39 +02:00
parent 5dc37ab239
commit cb3583e510
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
9 changed files with 149 additions and 52 deletions

View file

@ -70,6 +70,7 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement
if (state != State.fresh) { if (state != State.fresh) {
//Out of order (initiate after accept) //Out of order (initiate after accept)
LOGGER.log(Level.WARNING, "Action " + initiate.getAction() + " is out of order!");
return jutil.createErrorOutOfOrder(initiate); return jutil.createErrorOutOfOrder(initiate);
} }
@ -87,6 +88,7 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement
if (transportManager == null) { if (transportManager == null) {
//No usable transports. //No usable transports.
LOGGER.log(Level.WARNING, "No usable transports.");
jutil.sendSessionTerminateUnsupportedTransports(initiate.getInitiator(), initiate.getSid()); jutil.sendSessionTerminateUnsupportedTransports(initiate.getInitiator(), initiate.getSid());
state = State.terminated; state = State.terminated;
return jutil.createAck(initiate); return jutil.createAck(initiate);
@ -106,8 +108,9 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement
@Override @Override
public IQ handleTransportAccept(Jingle transportAccept) { public IQ handleTransportAccept(Jingle transportAccept) {
LOGGER.log(Level.INFO, "Received transport-accept.");
if (state != State.sent_transport_replace) { if (state != State.sent_transport_replace) {
LOGGER.log(Level.WARNING, "Session is in state " + state + ", so the transport-accept is out of order.");
return jutil.createErrorOutOfOrder(transportAccept); return jutil.createErrorOutOfOrder(transportAccept);
} }
@ -119,7 +122,7 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement
@Override @Override
public void acceptIncomingFileOffer(Jingle request, final File target) { public void acceptIncomingFileOffer(Jingle request, final File target) {
LOGGER.log(Level.INFO, "Client accepted incoming file offer. Try to start receiving.");
if (transportManager == null) { if (transportManager == null) {
//Unsupported transport //Unsupported transport
LOGGER.log(Level.WARNING, "Unsupported Transport method."); LOGGER.log(Level.WARNING, "Unsupported Transport method.");
@ -132,14 +135,16 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement
} }
final JingleContentTransport transport = transportManager.createTransport(request); final JingleContentTransport transport = transportManager.createTransport(request);
tasks.add(new Runnable() {
@Override
public void run() {
try { try {
jutil.sendSessionAccept(getInitiator(), sid, creator, name, JingleContent.Senders.initiator, file, transport);
state = State.active; state = State.active;
transportManager.initiateIncomingSession(getInitiator(), transport, new JingleTransportInitiationCallback() { transportManager.initiateIncomingSession(getInitiator(), transport, new JingleTransportInitiationCallback() {
@Override @Override
public void onSessionInitiated(BytestreamSession bytestreamSession) { public void onSessionInitiated(BytestreamSession bytestreamSession) {
receivingThread = new ReceivingThread(bytestreamSession, file, target); receivingThread = new ReceivingThread(bytestreamSession, file, target);
receivingThread.run(); receivingThread.start();
} }
@Override @Override
@ -147,10 +152,13 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement
} }
}); });
jutil.sendSessionAccept(getInitiator(), sid, creator, name, JingleContent.Senders.initiator, file, transport);
} catch (SmackException.NotConnectedException | SmackException.NoResponseException | XMPPException.XMPPErrorException | InterruptedException e) { } catch (SmackException.NotConnectedException | SmackException.NoResponseException | XMPPException.XMPPErrorException | InterruptedException e) {
LOGGER.log(Level.SEVERE, "Could not send session-accept: " + e, e); LOGGER.log(Level.SEVERE, "Could not send session-accept: " + e, e);
} }
} }
});
}
@Override @Override
public void declineIncomingFileOffer(Jingle request) { public void declineIncomingFileOffer(Jingle request) {

View file

@ -20,6 +20,8 @@ import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException;
@ -47,10 +49,11 @@ import org.jxmpp.jid.FullJid;
* Manager for JingleFileTransfer (XEP-0234). * Manager for JingleFileTransfer (XEP-0234).
*/ */
public final class JingleFileTransferManager extends Manager implements JingleHandler { public final class JingleFileTransferManager extends Manager implements JingleHandler {
private static final Logger LOGGER = Logger.getLogger(JingleFileTransferManager.class.getName());
private static final WeakHashMap<XMPPConnection, JingleFileTransferManager> INSTANCES = new WeakHashMap<>(); private static final WeakHashMap<XMPPConnection, JingleFileTransferManager> INSTANCES = new WeakHashMap<>();
private final JingleUtil jutil;
private final ArrayList<JingleFileTransferOfferListener> jingleFileTransferOfferListeners = new ArrayList<>(); private final ArrayList<JingleFileTransferOfferListener> jingleFileTransferOfferListeners = new ArrayList<>();
private final JingleUtil jutil;
private JingleFileTransferManager(XMPPConnection connection) { private JingleFileTransferManager(XMPPConnection connection) {
super(connection); super(connection);
@ -75,6 +78,7 @@ public final class JingleFileTransferManager extends Manager implements JingleHa
throws InterruptedException, XMPPException.XMPPErrorException, throws InterruptedException, XMPPException.XMPPErrorException,
SmackException.NotConnectedException, SmackException.NoResponseException { SmackException.NotConnectedException, SmackException.NoResponseException {
OutgoingJingleFileOffer offer = new OutgoingJingleFileOffer(connection(), recipient); OutgoingJingleFileOffer offer = new OutgoingJingleFileOffer(connection(), recipient);
JingleManager.getInstanceFor(connection()).registerJingleSessionHandler(recipient, offer.getSessionId(), offer);
offer.send(file); offer.send(file);
} }
@ -88,8 +92,6 @@ public final class JingleFileTransferManager extends Manager implements JingleHa
try { try {
handler = createSessionHandler(jingle); handler = createSessionHandler(jingle);
} catch (IllegalArgumentException malformed) { } catch (IllegalArgumentException malformed) {
// If senders is neither initiator, nor responder, consider session malformed.
// See XEP-0166 §6.3 Example 16 and XEP-0234 §4.1
return jutil.createErrorMalformedRequest(jingle); return jutil.createErrorMalformedRequest(jingle);
} }
@ -105,6 +107,7 @@ public final class JingleFileTransferManager extends Manager implements JingleHa
*/ */
private JingleFileTransferSession createSessionHandler(Jingle request) { private JingleFileTransferSession createSessionHandler(Jingle request) {
if (request.getAction() != JingleAction.session_initiate) { if (request.getAction() != JingleAction.session_initiate) {
LOGGER.log(Level.WARNING, "First received action must be session-initiate.");
throw new IllegalArgumentException("Requests action MUST be session-initiate."); throw new IllegalArgumentException("Requests action MUST be session-initiate.");
} }
@ -115,8 +118,11 @@ public final class JingleFileTransferManager extends Manager implements JingleHa
} //File Request } //File Request
else if (content.getSenders() == JingleContent.Senders.responder) { else if (content.getSenders() == JingleContent.Senders.responder) {
return JingleFileRequest.createIncomingFileRequest(connection(), request); return JingleFileRequest.createIncomingFileRequest(connection(), request);
} //Malformed Request
else { } else {
// If senders is neither initiator, nor responder, consider session malformed.
// See XEP-0166 §6.3 Example 16 and XEP-0234 §4.1
LOGGER.log(Level.WARNING, "Jingle has invalid sender value. Only initiator and responder are allowed.");
throw new IllegalArgumentException("Requests content.senders MUST be either responder or initiator."); throw new IllegalArgumentException("Requests content.senders MUST be either responder or initiator.");
} }
} }

View file

@ -43,15 +43,6 @@ public class OutgoingJingleFileOffer extends JingleFileTransferSession {
private static final Logger LOGGER = Logger.getLogger(OutgoingJingleFileOffer.class.getName()); private static final Logger LOGGER = Logger.getLogger(OutgoingJingleFileOffer.class.getName());
public void send(File file) throws InterruptedException, XMPPException.XMPPErrorException,
SmackException.NotConnectedException, SmackException.NoResponseException {
source = file;
String contentName = JingleManager.randomSid();
JingleFileTransfer transfer = JingleFileTransferManager.fileTransferFromFile(file);
initiateFileOffer(transfer, JingleContent.Creator.initiator, contentName);
}
public enum State { public enum State {
fresh, fresh,
pending, pending,
@ -74,6 +65,15 @@ public class OutgoingJingleFileOffer extends JingleFileTransferSession {
this(connection, recipient, JingleManager.randomSid()); this(connection, recipient, JingleManager.randomSid());
} }
public void send(File file) throws InterruptedException, XMPPException.XMPPErrorException,
SmackException.NotConnectedException, SmackException.NoResponseException {
source = file;
String contentName = JingleManager.randomSid();
JingleFileTransfer transfer = JingleFileTransferManager.fileTransferFromFile(file);
initiateFileOffer(transfer, JingleContent.Creator.initiator, contentName);
}
public void initiateFileOffer(JingleFileTransfer file, JingleContent.Creator creator, String name) throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException { public void initiateFileOffer(JingleFileTransfer file, JingleContent.Creator creator, String name) throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
if (state != State.fresh) { if (state != State.fresh) {
throw new IllegalStateException("This session is not fresh."); throw new IllegalStateException("This session is not fresh.");
@ -88,24 +88,30 @@ public class OutgoingJingleFileOffer extends JingleFileTransferSession {
transport = transportManager.createTransport(getResponder()); transport = transportManager.createTransport(getResponder());
jutil.sendSessionInitiateFileOffer(getResponder(), getSessionId(), creator, name, file, transport);
state = State.pending; state = State.pending;
jutil.sendSessionInitiateFileOffer(getResponder(), getSessionId(), creator, name, file, transport);
} }
@Override @Override
public IQ handleSessionAccept(Jingle sessionAccept) throws SmackException.NotConnectedException, InterruptedException { public IQ handleSessionAccept(Jingle sessionAccept) throws SmackException.NotConnectedException, InterruptedException {
// Out of order? // Out of order?
if (state != State.pending) { if (state != State.pending) {
LOGGER.log(Level.WARNING, "Out of order!"); LOGGER.log(Level.WARNING, "Session state is " + state + ", so session-accept is out of order.");
return jutil.createErrorOutOfOrder(sessionAccept); return jutil.createErrorOutOfOrder(sessionAccept);
} }
LOGGER.log(Level.INFO, "Session was accepted. Initiate Bytestream.");
state = State.active; state = State.active;
tasks.add(new Runnable() {
@Override
public void run() {
transportManager.initiateOutgoingSession(getResponder(), transport, new JingleTransportInitiationCallback() { transportManager.initiateOutgoingSession(getResponder(), transport, new JingleTransportInitiationCallback() {
@Override @Override
public void onSessionInitiated(final BytestreamSession session) { public void onSessionInitiated(final BytestreamSession session) {
LOGGER.log(Level.INFO, "BytestreamSession initiated. Start transfer.");
sendingThread = new SendingThread(session, source); sendingThread = new SendingThread(session, source);
sendingThread.run(); sendingThread.start();
} }
@Override @Override
@ -113,14 +119,18 @@ public class OutgoingJingleFileOffer extends JingleFileTransferSession {
LOGGER.log(Level.SEVERE, "Cannot create outgoing Bytestream session: ", e); LOGGER.log(Level.SEVERE, "Cannot create outgoing Bytestream session: ", e);
} }
}); });
}
});
return jutil.createAck(sessionAccept); return jutil.createAck(sessionAccept);
} }
@Override @Override
public IQ handleSessionTerminate(Jingle sessionTerminate) { public IQ handleSessionTerminate(Jingle sessionTerminate) {
LOGGER.log(Level.INFO, "Received session-terminate: " + sessionTerminate.getReason().asEnum());
if (sendingThread != null && !sendingThread.isInterrupted()) { if (sendingThread != null && !sendingThread.isInterrupted()) {
LOGGER.log(Level.INFO, "Interrupt sending thread.");
sendingThread.interrupt(); sendingThread.interrupt();
} }
@ -129,17 +139,33 @@ public class OutgoingJingleFileOffer extends JingleFileTransferSession {
} }
@Override @Override
public IQ handleTransportReplace(Jingle transportReplace) public IQ handleTransportReplace(final Jingle transportReplace)
throws InterruptedException, XMPPException.XMPPErrorException, throws InterruptedException, XMPPException.XMPPErrorException,
SmackException.NotConnectedException, SmackException.NoResponseException { SmackException.NotConnectedException, SmackException.NoResponseException {
JingleTransportManager<?> replacementManager = JingleTransportMethodManager.getInstanceFor(connection) LOGGER.log(Level.INFO, "Received transport-replace.");
final JingleTransportManager<?> replacementManager = JingleTransportMethodManager.getInstanceFor(connection)
.getTransportManager(transportReplace); .getTransportManager(transportReplace);
tasks.add(new Runnable() {
@Override
public void run() {
try {
if (replacementManager != null) { if (replacementManager != null) {
LOGGER.log(Level.INFO, "Accept transport-replace.");
jutil.sendTransportAccept(transportReplace.getFrom().asFullJidOrThrow(), jutil.sendTransportAccept(transportReplace.getFrom().asFullJidOrThrow(),
transportReplace.getInitiator(), transportReplace.getSid(), creator, name, transportReplace.getInitiator(), transportReplace.getSid(), creator, name,
replacementManager.createTransport(getResponder())); replacementManager.createTransport(getResponder()));
} else {
LOGGER.log(Level.INFO, "Unsupported transport. Reject transport-replace.");
jutil.sendTransportReject(transportReplace.getFrom().asFullJidOrThrow(), transportReplace.getInitiator(),
transportReplace.getSid(), creator, name, transportReplace.getContents().get(0).getJingleTransport());
} }
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
LOGGER.log(Level.SEVERE, "Help me please!", e);
}
}
});
return jutil.createAck(transportReplace); return jutil.createAck(transportReplace);
} }

View file

@ -134,6 +134,14 @@ public class JingleFileTransferChild extends JingleContentDescriptionChildElemen
return this; return this;
} }
/**
* Set the media type of the file.
* This is a MIME type from this list:
* https://www.iana.org/assignments/media-types/media-types.xhtml
* Default should be application/octet-stream.
* @param mediaType
* @return
*/
public Builder setMediaType(String mediaType) { public Builder setMediaType(String mediaType) {
this.mediaType = mediaType; this.mediaType = mediaType;
return this; return this;

View file

@ -19,6 +19,7 @@ package org.jivesoftware.smackx.jingle;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.Manager;
@ -33,7 +34,6 @@ import org.jivesoftware.smackx.jingle.element.JingleAction;
import org.jivesoftware.smackx.jingle.element.JingleContent; import org.jivesoftware.smackx.jingle.element.JingleContent;
import org.jivesoftware.smackx.jingle.element.JingleContentDescription; import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
import org.jivesoftware.smackx.jingle.transports.jingle_ibb.JingleIBBTransportManager; import org.jivesoftware.smackx.jingle.transports.jingle_ibb.JingleIBBTransportManager;
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.JingleS5BTransportManager;
import org.jxmpp.jid.FullJid; import org.jxmpp.jid.FullJid;
@ -88,19 +88,21 @@ public final class JingleManager extends Manager {
if (jingleDescriptionHandler == null) { if (jingleDescriptionHandler == null) {
//Unsupported Application //Unsupported Application
LOGGER.log(Level.WARNING, "Unsupported Jingle application.");
return jutil.createSessionTerminateUnsupportedApplications(fullFrom, sid); return jutil.createSessionTerminateUnsupportedApplications(fullFrom, sid);
} }
return jingleDescriptionHandler.handleJingleRequest(jingle); return jingleDescriptionHandler.handleJingleRequest(jingle);
} }
//Unknown session //Unknown session
LOGGER.log(Level.WARNING, "Unknown session.");
return jutil.createErrorUnknownSession(jingle); return jutil.createErrorUnknownSession(jingle);
} }
}); });
//Register transports. //Register transports.
JingleTransportMethodManager transportMethodManager = JingleTransportMethodManager.getInstanceFor(connection); JingleTransportMethodManager transportMethodManager = JingleTransportMethodManager.getInstanceFor(connection);
transportMethodManager.registerTransportManager(JingleIBBTransportManager.getInstanceFor(connection)); transportMethodManager.registerTransportManager(JingleIBBTransportManager.getInstanceFor(connection));
transportMethodManager.registerTransportManager(JingleS5BTransportManager.getInstanceFor(connection)); //transportMethodManager.registerTransportManager(JingleS5BTransportManager.getInstanceFor(connection));
} }
public JingleHandler registerDescriptionHandler(String namespace, JingleHandler handler) { public JingleHandler registerDescriptionHandler(String namespace, JingleHandler handler) {

View file

@ -16,6 +16,10 @@
*/ */
package org.jivesoftware.smackx.jingle; package org.jivesoftware.smackx.jingle;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
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.IQ; import org.jivesoftware.smack.packet.IQ;
@ -24,6 +28,7 @@ import org.jivesoftware.smackx.jingle.element.Jingle;
import org.jxmpp.jid.FullJid; import org.jxmpp.jid.FullJid;
public abstract class JingleSession implements JingleSessionHandler { public abstract class JingleSession implements JingleSessionHandler {
private static final Logger LOGGER = Logger.getLogger(JingleSession.class.getName());
protected final FullJid local; protected final FullJid local;
@ -33,6 +38,39 @@ public abstract class JingleSession implements JingleSessionHandler {
protected final String sid; protected final String sid;
protected final ConcurrentLinkedQueue<Runnable> tasks = new ConcurrentLinkedQueue<Runnable>() {
private static final long serialVersionUID = 1L;
@Override
public boolean add(Runnable runnable) {
synchronized (tasks) {
LOGGER.log(Level.INFO, "Add task.");
boolean b = super.add(runnable);
tasks.notify();
return b;
}
}
};
private final Thread worker = new Thread() {
@Override
public void run() {
synchronized (tasks) {
while (true) {
try {
tasks.wait();
while (!tasks.isEmpty()) {
LOGGER.log(Level.INFO, "Run task.");
tasks.poll().run();
}
} catch (InterruptedException e) {
LOGGER.log(Level.WARNING, "Interrupted.");
}
}
}
}
};
public JingleSession(FullJid initiator, FullJid responder, Role role, String sid) { public JingleSession(FullJid initiator, FullJid responder, Role role, String sid) {
if (role == Role.initiator) { if (role == Role.initiator) {
this.local = initiator; this.local = initiator;
@ -43,6 +81,7 @@ public abstract class JingleSession implements JingleSessionHandler {
} }
this.sid = sid; this.sid = sid;
this.role = role; this.role = role;
worker.start();
} }
public FullJid getInitiator() { public FullJid getInitiator() {

View file

@ -105,7 +105,7 @@ public class JingleUtil {
Jingle jingle = createSessionInitiate(recipient, sessionId, contentCreator, contentName, contentSenders, Jingle jingle = createSessionInitiate(recipient, sessionId, contentCreator, contentName, contentSenders,
description, transport); description, transport);
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); return connection.createStanzaCollectorAndSend(jingle).nextResult();
} }
public Jingle createSessionAccept(FullJid recipient, public Jingle createSessionAccept(FullJid recipient,
@ -148,7 +148,7 @@ public class JingleUtil {
Jingle jingle = createSessionAccept(recipient, sessionId, contentCreator, contentName, contentSenders, Jingle jingle = createSessionAccept(recipient, sessionId, contentCreator, contentName, contentSenders,
description, transport); description, transport);
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); return connection.createStanzaCollectorAndSend(jingle).nextResult();
} }
public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason reason) { public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason reason) {

View file

@ -122,6 +122,10 @@ public final class Jingle extends IQ {
return action; return action;
} }
public JingleReason getReason() {
return reason;
}
/** /**
* Get a List of the contents. * Get a List of the contents.
* *

View file

@ -124,6 +124,10 @@ public class JingleReason implements NamedElement {
return xml; return xml;
} }
public Reason asEnum() {
return reason;
}
public static class AlternativeSession extends JingleReason { public static class AlternativeSession extends JingleReason {