From cb3583e510d2d79f61e5e9e44edb09b7a334348a Mon Sep 17 00:00:00 2001 From: vanitasvitae Date: Thu, 22 Jun 2017 14:47:39 +0200 Subject: [PATCH] IBB sending works using worker thread --- .../IncomingJingleFileOffer.java | 42 ++++++---- .../JingleFileTransferManager.java | 16 ++-- .../OutgoingJingleFileOffer.java | 78 ++++++++++++------- .../element/JingleFileTransferChild.java | 8 ++ .../smackx/jingle/JingleManager.java | 6 +- .../smackx/jingle/JingleSession.java | 39 ++++++++++ .../smackx/jingle/JingleUtil.java | 4 +- .../smackx/jingle/element/Jingle.java | 4 + .../smackx/jingle/element/JingleReason.java | 4 + 9 files changed, 149 insertions(+), 52 deletions(-) diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/IncomingJingleFileOffer.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/IncomingJingleFileOffer.java index 66e12776f..6289945b0 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/IncomingJingleFileOffer.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/IncomingJingleFileOffer.java @@ -70,6 +70,7 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement if (state != State.fresh) { //Out of order (initiate after accept) + LOGGER.log(Level.WARNING, "Action " + initiate.getAction() + " is out of order!"); return jutil.createErrorOutOfOrder(initiate); } @@ -87,6 +88,7 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement if (transportManager == null) { //No usable transports. + LOGGER.log(Level.WARNING, "No usable transports."); jutil.sendSessionTerminateUnsupportedTransports(initiate.getInitiator(), initiate.getSid()); state = State.terminated; return jutil.createAck(initiate); @@ -106,8 +108,9 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement @Override public IQ handleTransportAccept(Jingle transportAccept) { - + LOGGER.log(Level.INFO, "Received transport-accept."); 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); } @@ -119,7 +122,7 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement @Override public void acceptIncomingFileOffer(Jingle request, final File target) { - + LOGGER.log(Level.INFO, "Client accepted incoming file offer. Try to start receiving."); if (transportManager == null) { //Unsupported transport LOGGER.log(Level.WARNING, "Unsupported Transport method."); @@ -132,24 +135,29 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement } final JingleContentTransport transport = transportManager.createTransport(request); - try { - jutil.sendSessionAccept(getInitiator(), sid, creator, name, JingleContent.Senders.initiator, file, transport); - state = State.active; - transportManager.initiateIncomingSession(getInitiator(), transport, new JingleTransportInitiationCallback() { - @Override - public void onSessionInitiated(BytestreamSession bytestreamSession) { - receivingThread = new ReceivingThread(bytestreamSession, file, target); - receivingThread.run(); - } + tasks.add(new Runnable() { + @Override + public void run() { + try { + state = State.active; + transportManager.initiateIncomingSession(getInitiator(), transport, new JingleTransportInitiationCallback() { + @Override + public void onSessionInitiated(BytestreamSession bytestreamSession) { + receivingThread = new ReceivingThread(bytestreamSession, file, target); + receivingThread.start(); + } - @Override - public void onException(Exception e) { + @Override + public void onException(Exception e) { + } + }); + jutil.sendSessionAccept(getInitiator(), sid, creator, name, JingleContent.Senders.initiator, file, transport); + } catch (SmackException.NotConnectedException | SmackException.NoResponseException | XMPPException.XMPPErrorException | InterruptedException e) { + LOGGER.log(Level.SEVERE, "Could not send session-accept: " + e, e); } - }); - } catch (SmackException.NotConnectedException | SmackException.NoResponseException | XMPPException.XMPPErrorException | InterruptedException e) { - LOGGER.log(Level.SEVERE, "Could not send session-accept: " + e, e); - } + } + }); } @Override diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java index 4ac2f79b1..74eaa1662 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java @@ -20,6 +20,8 @@ import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.SmackException; @@ -47,10 +49,11 @@ import org.jxmpp.jid.FullJid; * Manager for JingleFileTransfer (XEP-0234). */ public final class JingleFileTransferManager extends Manager implements JingleHandler { + private static final Logger LOGGER = Logger.getLogger(JingleFileTransferManager.class.getName()); private static final WeakHashMap INSTANCES = new WeakHashMap<>(); - private final JingleUtil jutil; private final ArrayList jingleFileTransferOfferListeners = new ArrayList<>(); + private final JingleUtil jutil; private JingleFileTransferManager(XMPPConnection connection) { super(connection); @@ -75,6 +78,7 @@ public final class JingleFileTransferManager extends Manager implements JingleHa throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException { OutgoingJingleFileOffer offer = new OutgoingJingleFileOffer(connection(), recipient); + JingleManager.getInstanceFor(connection()).registerJingleSessionHandler(recipient, offer.getSessionId(), offer); offer.send(file); } @@ -88,8 +92,6 @@ public final class JingleFileTransferManager extends Manager implements JingleHa try { handler = createSessionHandler(jingle); } 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); } @@ -105,6 +107,7 @@ public final class JingleFileTransferManager extends Manager implements JingleHa */ private JingleFileTransferSession createSessionHandler(Jingle request) { 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."); } @@ -115,8 +118,11 @@ public final class JingleFileTransferManager extends Manager implements JingleHa } //File Request else if (content.getSenders() == JingleContent.Senders.responder) { 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."); } } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/OutgoingJingleFileOffer.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/OutgoingJingleFileOffer.java index 621d1a325..625ed5653 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/OutgoingJingleFileOffer.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/OutgoingJingleFileOffer.java @@ -43,15 +43,6 @@ public class OutgoingJingleFileOffer extends JingleFileTransferSession { 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 { fresh, pending, @@ -74,6 +65,15 @@ public class OutgoingJingleFileOffer extends JingleFileTransferSession { 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 { if (state != State.fresh) { throw new IllegalStateException("This session is not fresh."); @@ -88,29 +88,37 @@ public class OutgoingJingleFileOffer extends JingleFileTransferSession { transport = transportManager.createTransport(getResponder()); - jutil.sendSessionInitiateFileOffer(getResponder(), getSessionId(), creator, name, file, transport); state = State.pending; + + jutil.sendSessionInitiateFileOffer(getResponder(), getSessionId(), creator, name, file, transport); } @Override public IQ handleSessionAccept(Jingle sessionAccept) throws SmackException.NotConnectedException, InterruptedException { // Out of order? 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); } + LOGGER.log(Level.INFO, "Session was accepted. Initiate Bytestream."); state = State.active; - transportManager.initiateOutgoingSession(getResponder(), transport, new JingleTransportInitiationCallback() { + tasks.add(new Runnable() { @Override - public void onSessionInitiated(final BytestreamSession session) { - sendingThread = new SendingThread(session, source); - sendingThread.run(); - } + public void run() { + transportManager.initiateOutgoingSession(getResponder(), transport, new JingleTransportInitiationCallback() { + @Override + public void onSessionInitiated(final BytestreamSession session) { + LOGGER.log(Level.INFO, "BytestreamSession initiated. Start transfer."); + sendingThread = new SendingThread(session, source); + sendingThread.start(); + } - @Override - public void onException(Exception e) { - LOGGER.log(Level.SEVERE, "Cannot create outgoing Bytestream session: ", e); + @Override + public void onException(Exception e) { + LOGGER.log(Level.SEVERE, "Cannot create outgoing Bytestream session: ", e); + } + }); } }); @@ -119,8 +127,10 @@ public class OutgoingJingleFileOffer extends JingleFileTransferSession { @Override public IQ handleSessionTerminate(Jingle sessionTerminate) { + LOGGER.log(Level.INFO, "Received session-terminate: " + sessionTerminate.getReason().asEnum()); if (sendingThread != null && !sendingThread.isInterrupted()) { + LOGGER.log(Level.INFO, "Interrupt sending thread."); sendingThread.interrupt(); } @@ -129,17 +139,33 @@ public class OutgoingJingleFileOffer extends JingleFileTransferSession { } @Override - public IQ handleTransportReplace(Jingle transportReplace) + public IQ handleTransportReplace(final Jingle transportReplace) throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException { - JingleTransportManager replacementManager = JingleTransportMethodManager.getInstanceFor(connection) + LOGGER.log(Level.INFO, "Received transport-replace."); + + final JingleTransportManager replacementManager = JingleTransportMethodManager.getInstanceFor(connection) .getTransportManager(transportReplace); - if (replacementManager != null) { - jutil.sendTransportAccept(transportReplace.getFrom().asFullJidOrThrow(), - transportReplace.getInitiator(), transportReplace.getSid(), creator, name, - replacementManager.createTransport(getResponder())); - } + tasks.add(new Runnable() { + @Override + public void run() { + try { + if (replacementManager != null) { + LOGGER.log(Level.INFO, "Accept transport-replace."); + jutil.sendTransportAccept(transportReplace.getFrom().asFullJidOrThrow(), + transportReplace.getInitiator(), transportReplace.getSid(), creator, name, + 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); } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransferChild.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransferChild.java index 9634d0a1b..ed20b38d8 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransferChild.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransferChild.java @@ -134,6 +134,14 @@ public class JingleFileTransferChild extends JingleContentDescriptionChildElemen 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) { this.mediaType = mediaType; return this; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleManager.java index 5962e8233..b4863c72c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleManager.java @@ -19,6 +19,7 @@ package org.jivesoftware.smackx.jingle; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; import java.util.logging.Logger; 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.JingleContentDescription; import org.jivesoftware.smackx.jingle.transports.jingle_ibb.JingleIBBTransportManager; -import org.jivesoftware.smackx.jingle.transports.jingle_s5b.JingleS5BTransportManager; import org.jxmpp.jid.FullJid; @@ -88,19 +88,21 @@ public final class JingleManager extends Manager { if (jingleDescriptionHandler == null) { //Unsupported Application + LOGGER.log(Level.WARNING, "Unsupported Jingle application."); return jutil.createSessionTerminateUnsupportedApplications(fullFrom, sid); } return jingleDescriptionHandler.handleJingleRequest(jingle); } //Unknown session + LOGGER.log(Level.WARNING, "Unknown session."); return jutil.createErrorUnknownSession(jingle); } }); //Register transports. JingleTransportMethodManager transportMethodManager = JingleTransportMethodManager.getInstanceFor(connection); transportMethodManager.registerTransportManager(JingleIBBTransportManager.getInstanceFor(connection)); - transportMethodManager.registerTransportManager(JingleS5BTransportManager.getInstanceFor(connection)); + //transportMethodManager.registerTransportManager(JingleS5BTransportManager.getInstanceFor(connection)); } public JingleHandler registerDescriptionHandler(String namespace, JingleHandler handler) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java index efaa52493..b263387f1 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java @@ -16,6 +16,10 @@ */ 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.XMPPException; import org.jivesoftware.smack.packet.IQ; @@ -24,6 +28,7 @@ import org.jivesoftware.smackx.jingle.element.Jingle; import org.jxmpp.jid.FullJid; public abstract class JingleSession implements JingleSessionHandler { + private static final Logger LOGGER = Logger.getLogger(JingleSession.class.getName()); protected final FullJid local; @@ -33,6 +38,39 @@ public abstract class JingleSession implements JingleSessionHandler { protected final String sid; + protected final ConcurrentLinkedQueue tasks = new ConcurrentLinkedQueue() { + 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) { if (role == Role.initiator) { this.local = initiator; @@ -43,6 +81,7 @@ public abstract class JingleSession implements JingleSessionHandler { } this.sid = sid; this.role = role; + worker.start(); } public FullJid getInitiator() { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java index 994bdcff7..8f47fea94 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java @@ -105,7 +105,7 @@ public class JingleUtil { Jingle jingle = createSessionInitiate(recipient, sessionId, contentCreator, contentName, contentSenders, description, transport); - return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + return connection.createStanzaCollectorAndSend(jingle).nextResult(); } public Jingle createSessionAccept(FullJid recipient, @@ -148,7 +148,7 @@ public class JingleUtil { Jingle jingle = createSessionAccept(recipient, sessionId, contentCreator, contentName, contentSenders, description, transport); - return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + return connection.createStanzaCollectorAndSend(jingle).nextResult(); } public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason reason) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java index 8e8b2f580..3106563d8 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java @@ -122,6 +122,10 @@ public final class Jingle extends IQ { return action; } + public JingleReason getReason() { + return reason; + } + /** * Get a List of the contents. * diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java index a975968b2..b91eb758b 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java @@ -124,6 +124,10 @@ public class JingleReason implements NamedElement { return xml; } + public Reason asEnum() { + return reason; + } + public static class AlternativeSession extends JingleReason {