diff --git a/source/org/jivesoftware/smackx/filetransfer/FileTransfer.java b/source/org/jivesoftware/smackx/filetransfer/FileTransfer.java index ac5eaebbb..a287d0ddc 100644 --- a/source/org/jivesoftware/smackx/filetransfer/FileTransfer.java +++ b/source/org/jivesoftware/smackx/filetransfer/FileTransfer.java @@ -28,9 +28,9 @@ import org.jivesoftware.smack.XMPPException; /** * Contains the generic file information and progress related to a particular * file transfer. - * + * * @author Alexander Wenckus - * + * */ public abstract class FileTransfer { @@ -44,6 +44,8 @@ public abstract class FileTransfer { private org.jivesoftware.smackx.filetransfer.FileTransfer.Status status; + private final Object statusMonitor = new Object(); + protected FileTransferNegotiator negotiator; protected String streamID; @@ -74,7 +76,7 @@ public abstract class FileTransfer { /** * Returns the size of the file being transfered. - * + * * @return Returns the size of the file being transfered. */ public long getFileSize() { @@ -83,7 +85,7 @@ public abstract class FileTransfer { /** * Returns the name of the file being transfered. - * + * * @return Returns the name of the file being transfered. */ public String getFileName() { @@ -92,7 +94,7 @@ public abstract class FileTransfer { /** * Returns the local path of the file. - * + * * @return Returns the local path of the file. */ public String getFilePath() { @@ -101,7 +103,7 @@ public abstract class FileTransfer { /** * Returns the JID of the peer for this file transfer. - * + * * @return Returns the JID of the peer for this file transfer. */ public String getPeer() { @@ -110,21 +112,21 @@ public abstract class FileTransfer { /** * Returns the progress of the file transfer as a number between 0 and 1. - * + * * @return Returns the progress of the file transfer as a number between 0 * and 1. */ public double getProgress() { - if(amountWritten == 0) { + if (amountWritten <= 0 || fileSize <= 0) { return 0; } - return amountWritten / fileSize; + return (double) amountWritten / (double) fileSize; } /** * Returns true if the transfer has been cancled, if it has stopped because * of a an error, or the transfer completed succesfully. - * + * * @return Returns true if the transfer has been cancled, if it has stopped * because of a an error, or the transfer completed succesfully. */ @@ -135,7 +137,7 @@ public abstract class FileTransfer { /** * Retuns the current status of the file transfer. - * + * * @return Retuns the current status of the file transfer. */ public Status getStatus() { @@ -150,7 +152,7 @@ public abstract class FileTransfer { * When {@link #getStatus()} returns that there was an {@link Status#ERROR} * during the transfer, the type of error can be retrieved through this * method. - * + * * @return Returns the type of error that occured if one has occured. */ public Error getError() { @@ -160,7 +162,7 @@ public abstract class FileTransfer { /** * If an exception occurs asynchronously it will be stored for later * retrival. If there is an error there maybe an exception set. - * + * * @return The exception that occured or null if there was no exception. * @see #getError() */ @@ -178,13 +180,25 @@ public abstract class FileTransfer { } protected final void setStatus(Status status) { - this.status = status; - } + synchronized (statusMonitor) { + this.status = status; + } + } + + protected final boolean updateStatus(Status oldStatus, Status newStatus) { + synchronized (statusMonitor) { + if (oldStatus != status) { + return false; + } + status = newStatus; + return true; + } + } protected void writeToStream(final InputStream in, final OutputStream out) throws XMPPException { final byte[] b = new byte[1000]; - int count = 0; + int count; amountWritten = 0; try { count = in.read(b); @@ -217,30 +231,37 @@ public abstract class FileTransfer { // equal if (!getStatus().equals(Status.CANCLED) && getError() == Error.NONE && amountWritten != fileSize) { + setStatus(Status.ERROR); this.error = Error.CONNECTION; } } /** * A class to represent the current status of the file transfer. - * + * * @author Alexander Wenckus - * + * */ public static class Status { + /** * An error occured during the transfer. - * + * * @see FileTransfer#getError() */ public static final Status ERROR = new Status(); /** + * The initial status of the file transfer. + */ + public static final Status INITIAL = new Status(); + + /** * The file transfer is being negotiated with the peer. The party * recieving the file has the option to accept or refuse a file transfer * request. If they accept, then the process of stream negotiation will * begin. If they refuse the file will not be transfered. - * + * * @see #NEGOTIATING_STREAM */ public static final Status NEGOTIATING_TRANSFER = new Status(); @@ -255,7 +276,7 @@ public abstract class FileTransfer { * The stream to transfer the file is being negotiated over the chosen * stream type. After the stream negotiating process is complete the * status becomes negotiated. - * + * * @see #NEGOTIATED */ public static final Status NEGOTIATING_STREAM = new Status(); @@ -269,7 +290,7 @@ public abstract class FileTransfer { /** * The transfer is in progress. - * + * * @see FileTransfer#getProgress() */ public static final Status IN_PROGRESS = new Status(); @@ -338,7 +359,7 @@ public abstract class FileTransfer { /** * Returns a String representation of this error. - * + * * @return Returns a String representation of this error. */ public String getMessage() { diff --git a/source/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java b/source/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java index 0774904a3..cc378edba 100644 --- a/source/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java +++ b/source/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java @@ -149,7 +149,7 @@ public class FileTransferNegotiator { * @param type The iq type of the packet. * @return The created IQ packet. */ - protected static IQ createIQ(final String ID, final String to, + public static IQ createIQ(final String ID, final String to, final String from, final IQ.Type type) { IQ iqPacket = new IQ() { public String getChildElementXML() { diff --git a/source/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java b/source/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java index d38e77e64..0897d5e83 100644 --- a/source/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java +++ b/source/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java @@ -19,23 +19,18 @@ */ package org.jivesoftware.smackx.filetransfer; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.XMPPError; +import java.io.*; + /** * Handles the sending of a file to another user. File transfer's in jabber have * several steps and there are several methods in this class that handle these * steps differently. - * + * * @author Alexander Wenckus - * + * */ public class OutgoingFileTransfer extends FileTransfer { @@ -44,7 +39,7 @@ public class OutgoingFileTransfer extends FileTransfer { /** * Returns the time in milliseconds after which the file transfer * negotiation process will timeout if the other user has not responded. - * + * * @return Returns the time in milliseconds after which the file transfer * negotiation process will timeout if the remote user has not * responded. @@ -56,7 +51,7 @@ public class OutgoingFileTransfer extends FileTransfer { /** * Sets the time in milliseconds after which the file transfer negotiation * process will timeout if the other user has not responded. - * + * * @param responseTimeout * The timeout time in milliseconds. */ @@ -86,7 +81,7 @@ public class OutgoingFileTransfer extends FileTransfer { * Returns the output stream connected to the peer to transfer the file. It * is only available after it has been succesfully negotiated by the * {@link StreamNegotiator}. - * + * * @return Returns the output stream connected to the peer to transfer the * file. */ @@ -101,7 +96,7 @@ public class OutgoingFileTransfer extends FileTransfer { /** * This method handles the negotiation of the file transfer and the stream, * it only returns the created stream after the negotiation has been completed. - * + * * @param fileName * The name of the file that will be transmitted. It is * preferable for this name to have an extension as it will be @@ -138,7 +133,7 @@ public class OutgoingFileTransfer extends FileTransfer { * {@link NegotiationProgress} callback. When the negotiation process is * complete the OutputStream can be retrieved from the callback via the * {@link NegotiationProgress#getOutputStream()} method. - * + * * @param fileName * The name of the file that will be transmitted. It is * preferable for this name to have an extension as it will be @@ -186,13 +181,13 @@ public class OutgoingFileTransfer extends FileTransfer { * This method handles the stream negotiation process and transmits the file * to the remote user. It returns immediatly and the progress of the file * transfer can be monitored through several methods: - * + * * - * + * * @throws XMPPException * If there is an error during the negotiation process or the * sending of the file. @@ -219,10 +214,9 @@ public class OutgoingFileTransfer extends FileTransfer { return; } - if (!getStatus().equals(Status.NEGOTIATED)) { + if (!updateStatus(Status.NEGOTIATED, Status.IN_PROGRESS)) { return; } - setStatus(Status.IN_PROGRESS); InputStream inputStream = null; try { @@ -244,12 +238,11 @@ public class OutgoingFileTransfer extends FileTransfer { outputStream.flush(); outputStream.close(); } catch (IOException e) { + /* Do Nothing */ } } - if (getStatus().equals(Status.IN_PROGRESS)) { - setStatus(FileTransfer.Status.COMPLETE); + updateStatus(Status.IN_PROGRESS, FileTransfer.Status.COMPLETE); } - } }, "File Transfer " + streamID); transferThread.start(); @@ -279,7 +272,7 @@ public class OutgoingFileTransfer extends FileTransfer { * Note: This method is only useful when the {@link #sendFile(File, String)} * method is called, as it is the only method that actualy transmits the * file. - * + * * @return Returns the amount of bytes that have been sent for the file * transfer. Or -1 if the file transfer has not started. */ @@ -291,7 +284,9 @@ public class OutgoingFileTransfer extends FileTransfer { String description) throws XMPPException { // Negotiate the file transfer profile - setStatus(Status.NEGOTIATING_TRANSFER); + if (!updateStatus(Status.INITIAL, Status.NEGOTIATING_TRANSFER)) { + throw new XMPPException("Illegal state change"); + } StreamNegotiator streamNegotiator = negotiator.negotiateOutgoingTransfer( getPeer(), streamID, fileName, fileSize, description, RESPONSE_TIMEOUT); @@ -302,19 +297,16 @@ public class OutgoingFileTransfer extends FileTransfer { return null; } - if (!getStatus().equals(Status.NEGOTIATING_TRANSFER)) { - return null; - } - - // Negotiate the stream - - setStatus(Status.NEGOTIATING_STREAM); + // Negotiate the stream + if (!updateStatus(Status.NEGOTIATING_TRANSFER, Status.NEGOTIATING_STREAM)) { + throw new XMPPException("Illegal state change"); + } outputStream = streamNegotiator.createOutgoingStream(streamID, initiator, getPeer()); - if (!getStatus().equals(Status.NEGOTIATING_STREAM)) { - return null; + + if (!updateStatus(Status.NEGOTIATING_STREAM, Status.NEGOTIATED)) { + throw new XMPPException("Illegal state change"); } - setStatus(Status.NEGOTIATED); return outputStream; } @@ -325,9 +317,9 @@ public class OutgoingFileTransfer extends FileTransfer { /** * A callback class to retrive the status of an outgoing transfer * negotiation process. - * + * * @author Alexander Wenckus - * + * */ public static class NegotiationProgress { @@ -335,7 +327,7 @@ public class OutgoingFileTransfer extends FileTransfer { /** * Returns the current status of the negotiation process. - * + * * @return Returns the current status of the negotiation process. */ public Status getStatus() { @@ -348,10 +340,10 @@ public class OutgoingFileTransfer extends FileTransfer { /** * Once the negotiation process is completed the output stream can be * retrieved. - * + * * @return Once the negotiation process is completed the output stream * can be retrieved. - * + * */ public OutputStream getOutputStream() { if (delegate == null) { diff --git a/source/org/jivesoftware/smackx/packet/Bytestream.java b/source/org/jivesoftware/smackx/packet/Bytestream.java index 070157b2e..4987cacab 100644 --- a/source/org/jivesoftware/smackx/packet/Bytestream.java +++ b/source/org/jivesoftware/smackx/packet/Bytestream.java @@ -244,6 +244,12 @@ public class Bytestream extends IQ { buf.append(">"); if (getUsedHost() != null) buf.append(getUsedHost().toXML()); + // A result from the server can also contain stream hosts + else if (countStreamHosts() > 0) { + for (Iterator it = getStreamHosts().iterator(); it.hasNext();) { + buf.append(((StreamHost) it.next()).toXML()); + } + } } else { return null;