Use handler to notify on finished file transfer

This commit is contained in:
vanitasvitae 2017-07-10 12:46:51 +02:00
parent 63d71230cc
commit f9086439a8
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
14 changed files with 163 additions and 36 deletions

View File

@ -30,10 +30,12 @@ import org.jivesoftware.smackx.jingle.JingleTransportMethodManager;
import org.jivesoftware.smackx.jingle.Role;
import org.jivesoftware.smackx.jingle.element.Jingle;
import org.jivesoftware.smackx.jingle.element.JingleContent;
import org.jivesoftware.smackx.jingle.element.JingleReason;
import org.jivesoftware.smackx.jingle.transports.JingleTransportInitiationCallback;
import org.jivesoftware.smackx.jingle.transports.JingleTransportManager;
import org.jivesoftware.smackx.jingle_filetransfer.callback.IncomingFileOfferCallback;
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransfer;
import org.jivesoftware.smackx.jingle_filetransfer.handler.FileTransferHandler;
import org.jxmpp.jid.FullJid;
@ -46,6 +48,12 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement
private ReceiveTask receivingThread;
private File target;
@Override
public void cancel() {
//TODO: Actually cancel
notifyEndedListeners(JingleReason.Reason.cancel);
}
public enum State {
fresh,
pending,
@ -132,7 +140,7 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement
@Override
public void onSessionInitiated(BytestreamSession bytestreamSession) {
LOGGER.log(Level.INFO, "Bytestream initiated. Start receiving.");
receivingThread = new ReceiveTask(bytestreamSession, file, target);
receivingThread = new ReceiveTask(IncomingJingleFileOffer.this, bytestreamSession, file, target);
queued.add(JingleManager.getThreadPool().submit(receivingThread));
}
@ -183,7 +191,7 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement
}
@Override
public void acceptIncomingFileOffer(final Jingle request, final File target) {
public FileTransferHandler acceptIncomingFileOffer(final Jingle request, final File target) {
this.target = target;
LOGGER.log(Level.INFO, "Client accepted incoming file offer. Try to start receiving.");
if (transportSession == null) {
@ -195,7 +203,7 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement
SmackException.NotConnectedException | XMPPException.XMPPErrorException e) {
LOGGER.log(Level.SEVERE, "Could not send session-terminate: " + e, e);
}
return;
return null;
}
state = State.active;
@ -213,8 +221,10 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement
@Override
public void onSessionInitiated(BytestreamSession bytestreamSession) {
LOGGER.log(Level.INFO, "Bytestream initiated. Start receiving.");
receivingThread = new ReceiveTask(bytestreamSession, file, target);
receivingThread = new ReceiveTask(IncomingJingleFileOffer.this, bytestreamSession, file, target);
queued.add(JingleManager.getThreadPool().submit(receivingThread));
started = true;
notifyStartedListeners();
}
@Override
@ -222,6 +232,8 @@ public class IncomingJingleFileOffer extends JingleFileTransferSession implement
LOGGER.log(Level.SEVERE, "EXCEPTION IN INCOMING SESSION: ", e);
}
});
return this;
}
@Override

View File

@ -46,4 +46,9 @@ public class JingleFileRequest extends JingleFileTransferSession {
public void onTransportMethodFailed(String namespace) {
//Not implemented
}
@Override
public void cancel() {
}
}

View File

@ -25,7 +25,6 @@ import java.util.logging.Logger;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackFuture;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
@ -41,7 +40,6 @@ import org.jivesoftware.smackx.jingle.provider.JingleContentProviderManager;
import org.jivesoftware.smackx.jingle_filetransfer.callback.IncomingFileOfferCallback;
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransfer;
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChild;
import org.jivesoftware.smackx.jingle_filetransfer.handler.FileTransferHandler;
import org.jivesoftware.smackx.jingle_filetransfer.listener.JingleFileTransferOfferListener;
import org.jivesoftware.smackx.jingle_filetransfer.provider.JingleFileTransferProvider;
@ -83,14 +81,7 @@ public final class JingleFileTransferManager extends Manager implements JingleHa
OutgoingJingleFileOffer offer = new OutgoingJingleFileOffer(connection(), recipient);
JingleManager.getInstanceFor(connection()).registerJingleSessionHandler(recipient, offer.getSessionId(), offer);
offer.send(file);
//return offer;
return null;
}
public SmackFuture<?> asyncSendFile(FullJid recipient, File file) {
OutgoingJingleFileOffer offer = new OutgoingJingleFileOffer(connection(), recipient);
JingleManager.getInstanceFor(connection()).registerJingleSessionHandler(recipient, offer.getSessionId(), offer);
return offer.sendAsync(file);
return offer;
}
@Override

View File

@ -16,18 +16,27 @@
*/
package org.jivesoftware.smackx.jingle_filetransfer;
import java.util.ArrayList;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smackx.jingle.JingleSession;
import org.jivesoftware.smackx.jingle.JingleUtil;
import org.jivesoftware.smackx.jingle.Role;
import org.jivesoftware.smackx.jingle.element.JingleReason;
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransfer;
import org.jivesoftware.smackx.jingle_filetransfer.handler.FileTransferHandler;
import org.jxmpp.jid.FullJid;
/**
* Class representing a Jingle session in the context of Jingle File Transfer (XEP-0234).
*/
public abstract class JingleFileTransferSession extends JingleSession {
public abstract class JingleFileTransferSession extends JingleSession implements FileTransferHandler {
protected final ArrayList<EndedListener> endedListeners = new ArrayList<>();
protected final ArrayList<StartedListener> startedListeners = new ArrayList<>();
protected boolean started, ended;
public enum Type {
offer,
@ -69,6 +78,42 @@ public abstract class JingleFileTransferSession extends JingleSession {
return (isRequest() && isInitiator()) || (isOffer() && isResponder());
}
@Override
public boolean isFinished() {
return ended;
}
@Override
public boolean isStarted() {
return started;
}
@Override
public void addEndedListener(EndedListener listener) {
endedListeners.add(listener);
}
@Override
public void addStartedListener(StartedListener listener) {
startedListeners.add(listener);
}
@Override
public void notifyEndedListeners(JingleReason.Reason reason) {
ended = true;
for (EndedListener e : endedListeners) {
e.onEnded(reason);
}
}
@Override
public void notifyStartedListeners() {
started = true;
for (StartedListener s : startedListeners) {
s.onStarted();
}
}
@Override
public XMPPConnection getConnection() {
return connection;

View File

@ -32,6 +32,7 @@ import org.jivesoftware.smackx.jingle.JingleTransportMethodManager;
import org.jivesoftware.smackx.jingle.Role;
import org.jivesoftware.smackx.jingle.element.Jingle;
import org.jivesoftware.smackx.jingle.element.JingleContent;
import org.jivesoftware.smackx.jingle.element.JingleReason;
import org.jivesoftware.smackx.jingle.transports.JingleTransportInitiationCallback;
import org.jivesoftware.smackx.jingle.transports.JingleTransportManager;
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransfer;
@ -45,6 +46,12 @@ public class OutgoingJingleFileOffer extends JingleFileTransferSession {
private static final Logger LOGGER = Logger.getLogger(OutgoingJingleFileOffer.class.getName());
@Override
public void cancel() {
//TODO: Actually cancel
notifyEndedListeners(JingleReason.Reason.cancel);
}
public enum State {
fresh,
pending,
@ -119,9 +126,10 @@ public class OutgoingJingleFileOffer extends JingleFileTransferSession {
transportSession.initiateOutgoingSession(new JingleTransportInitiationCallback() {
@Override
public void onSessionInitiated(final BytestreamSession session) {
sendingThread = new SendTask(session, source);
public void onSessionInitiated(final BytestreamSession byteStream) {
sendingThread = new SendTask(OutgoingJingleFileOffer.this, byteStream, source);
queued.add(JingleManager.getThreadPool().submit(sendingThread));
notifyStartedListeners();
}
@Override

View File

@ -24,6 +24,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
import org.jivesoftware.smackx.jingle.element.JingleReason;
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransfer;
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChild;
@ -33,14 +34,16 @@ import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChi
public class ReceiveTask implements Runnable {
private static final Logger LOGGER = Logger.getLogger(ReceiveTask.class.getName());
private final BytestreamSession session;
private final BytestreamSession byteStream;
private final JingleFileTransfer fileTransfer;
private final File target;
private final JingleFileTransferSession session;
public ReceiveTask(BytestreamSession session, JingleFileTransfer fileTransfer, File target) {
this.session = session;
public ReceiveTask(JingleFileTransferSession session, BytestreamSession byteStream, JingleFileTransfer fileTransfer, File target) {
this.byteStream = byteStream;
this.fileTransfer = fileTransfer;
this.target = target;
this.session = session;
}
@Override
@ -51,7 +54,7 @@ public class ReceiveTask implements Runnable {
try {
outputStream = new FileOutputStream(target);
inputStream = session.getInputStream();
inputStream = byteStream.getInputStream();
byte[] filebuf = new byte[transfer.getSize()];
int read = 0;
@ -75,7 +78,7 @@ public class ReceiveTask implements Runnable {
LOGGER.log(Level.SEVERE, "Error while receiving data: ", e);
} finally {
try {
session.close();
byteStream.close();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not close InputStream.", e);
}
@ -86,6 +89,8 @@ public class ReceiveTask implements Runnable {
LOGGER.log(Level.SEVERE, "Could not close FileOutputStream.", e);
}
}
session.notifyEndedListeners(JingleReason.Reason.success);
}
}
}

View File

@ -25,6 +25,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
import org.jivesoftware.smackx.jingle.element.JingleReason;
/**
* Created by vanitas on 21.06.17.
@ -32,12 +33,14 @@ import org.jivesoftware.smackx.bytestreams.BytestreamSession;
public class SendTask implements Runnable {
private static final Logger LOGGER = Logger.getLogger(SendTask.class.getName());
private final BytestreamSession session;
private final BytestreamSession byteStream;
private final JingleFileTransferSession session;
private final File source;
public SendTask(BytestreamSession session, File source) {
this.session = session;
public SendTask(JingleFileTransferSession session, BytestreamSession byteStream, File source) {
this.byteStream = byteStream;
this.source = source;
this.session = session;
}
@Override
@ -47,7 +50,7 @@ public class SendTask implements Runnable {
try {
inputStream = new FileInputStream(source);
outputStream = session.getOutputStream();
outputStream = byteStream.getOutputStream();
byte[] filebuf = new byte[(int) source.length()];
int r = inputStream.read(filebuf);
@ -74,6 +77,8 @@ public class SendTask implements Runnable {
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not close session.", e);
}
session.notifyEndedListeners(JingleReason.Reason.success);
}
}
}

View File

@ -19,13 +19,14 @@ package org.jivesoftware.smackx.jingle_filetransfer.callback;
import java.io.File;
import org.jivesoftware.smackx.jingle.element.Jingle;
import org.jivesoftware.smackx.jingle_filetransfer.handler.FileTransferHandler;
/**
* Callback used to accept/decline file offers.
*/
public interface IncomingFileOfferCallback {
void acceptIncomingFileOffer(Jingle request, File target);
FileTransferHandler acceptIncomingFileOffer(Jingle request, File target);
void declineIncomingFileOffer(Jingle request);
}

View File

@ -29,8 +29,8 @@ public interface FileTransferHandler {
void cancel();
/**
* Returns true, if the file transfer is finished.
* @return true if transfer finished.
* Returns true, if the file transfer is ended.
* @return true if transfer ended.
*/
boolean isFinished();
@ -53,7 +53,7 @@ public interface FileTransferHandler {
void addStartedListener(StartedListener listener);
/**
* Notify all registered FinishedListeners that the file transfer has finished.
* Notify all registered FinishedListeners that the file transfer has ended.
*/
void notifyEndedListeners(JingleReason.Reason reason);
@ -63,7 +63,7 @@ public interface FileTransferHandler {
void notifyStartedListeners();
/**
* A FinishedListener will be notified by the SendFileHandler when the corresponding file transfer is finished.
* A FinishedListener will be notified by the SendFileHandler when the corresponding file transfer is ended.
*/
interface EndedListener {
void onEnded(JingleReason.Reason reason);

View File

@ -1,9 +1,25 @@
/**
*
* Copyright 2017 Paul Schaub
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle;
/**
* Created by vanitas on 06.07.17.
*/
public class JingleStreamRouter {
public final class JingleStreamRouter {
private static JingleStreamRouter INSTANCE;
private JingleStreamRouter() {

View File

@ -25,6 +25,14 @@ public class JingleContentProviderManager {
private static final Map<String, JingleContentTransportProvider<?>> jingleContentTransportProviders = new ConcurrentHashMap<>();
public static void removeJingleContentTransportProvider(String namespace) {
jingleContentTransportProviders.remove(namespace);
}
public static void removeJingleContentDescriptionProvider(String namespace) {
jingleContentDescriptionProviders.remove(namespace);
}
public static JingleContentDescriptionProvider<?> addJingleContentDescriptionProvider(String namespace,
JingleContentDescriptionProvider<?> provider) {
return jingleContentDescriptionProviders.put(namespace, provider);

View File

@ -35,6 +35,9 @@ public class JingleContentProviderManagerTest extends SmackTestSuite {
@Test
public void transportProviderTest() {
JingleContentProviderManager.removeJingleContentTransportProvider(JingleIBBTransport.NAMESPACE_V1);
JingleContentProviderManager.removeJingleContentTransportProvider(JingleS5BTransport.NAMESPACE_V1);
assertNull(JingleContentProviderManager.getJingleContentTransportProvider(JingleIBBTransport.NAMESPACE_V1));
assertNull(JingleContentProviderManager.getJingleContentTransportProvider(JingleS5BTransport.NAMESPACE_V1));

View File

@ -254,7 +254,7 @@ public class JingleUtilTest extends SmackTestSuite {
String xml =
"<iq " +
"to='" + romeo + "' " +
"from='" + romeo +"' " +
"from='" + romeo + "' " +
"id='" + j.getStanzaId() + "' " +
"type='error'>" +
"<error type='cancel'>" +

View File

@ -29,12 +29,15 @@ import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy;
import org.jivesoftware.smackx.jingle.element.Jingle;
import org.jivesoftware.smackx.jingle.element.JingleReason;
import org.jivesoftware.smackx.jingle_filetransfer.callback.IncomingFileOfferCallback;
import org.jivesoftware.smackx.jingle_filetransfer.handler.FileTransferHandler;
import org.jivesoftware.smackx.jingle_filetransfer.listener.JingleFileTransferOfferListener;
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
import org.junit.AfterClass;
import org.jxmpp.jid.FullJid;
@ -61,6 +64,8 @@ public class FileTransferTest extends AbstractSmackIntegrationTest {
@SmackIntegrationTest
public void basicFileTransferTest() {
final SimpleResultSyncPoint resultSyncPoint1 = new SimpleResultSyncPoint();
final SimpleResultSyncPoint resultSyncPoint2 = new SimpleResultSyncPoint();
FullJid alice = conOne.getUser().asFullJidOrThrow();
FullJid bob = conTwo.getUser().asFullJidOrThrow();
@ -74,16 +79,40 @@ public class FileTransferTest extends AbstractSmackIntegrationTest {
bftm.addJingleFileTransferOfferListener(new JingleFileTransferOfferListener() {
@Override
public void onFileOffer(Jingle request, IncomingFileOfferCallback callback) {
callback.acceptIncomingFileOffer(request, target);
FileTransferHandler handler2 = callback.acceptIncomingFileOffer(request, target);
handler2.addEndedListener(new FileTransferHandler.EndedListener() {
@Override
public void onEnded(JingleReason.Reason reason) {
resultSyncPoint2.signal();
}
});
}
});
try {
aftm.sendFile(bob, source);
FileTransferHandler handler = aftm.sendFile(bob, source);
handler.addEndedListener(new FileTransferHandler.EndedListener() {
@Override
public void onEnded(JingleReason.Reason reason) {
resultSyncPoint1.signal();
}
});
} catch (InterruptedException | SmackException.NoResponseException | SmackException.NotConnectedException | XMPPException.XMPPErrorException e) {
fail();
}
try {
resultSyncPoint1.waitForResult(10 * 1000);
} catch (Exception e) {
fail();
}
try {
resultSyncPoint2.waitForResult(10 * 1000);
} catch (Exception e) {
fail();
}
byte[] sBytes = new byte[(int) source.length()];
byte[] tBytes = new byte[(int) target.length()];
try {
@ -92,7 +121,6 @@ public class FileTransferTest extends AbstractSmackIntegrationTest {
fi.close();
fi = new FileInputStream(target);
fi.read(tBytes);
fi.close();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not read files.");
fail();