FileTransfer.java

  1. /**
  2.  *
  3.  * Copyright 2003-2006 Jive Software.
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.filetransfer;

  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.io.OutputStream;

  21. import org.jxmpp.jid.Jid;

  22. /**
  23.  * Contains the generic file information and progress related to a particular
  24.  * file transfer.
  25.  *
  26.  * @author Alexander Wenckus
  27.  *
  28.  */
  29. public abstract class FileTransfer {

  30.     private String fileName;

  31.     private String filePath;

  32.     private long fileSize;

  33.     private Jid peer;

  34.     private Status status = Status.initial;

  35.     private final Object statusMonitor = new Object();

  36.     protected FileTransferNegotiator negotiator;

  37.     protected String streamID;

  38.     protected long amountWritten = -1;

  39.     private Error error;

  40.     private Exception exception;

  41.     /**
  42.      * Buffer size between input and output
  43.      */
  44.     private static final int BUFFER_SIZE = 8192;

  45.     protected FileTransfer(Jid peer, String streamID,
  46.             FileTransferNegotiator negotiator) {
  47.         this.peer = peer;
  48.         this.streamID = streamID;
  49.         this.negotiator = negotiator;
  50.     }

  51.     protected void setFileInfo(String fileName, long fileSize) {
  52.         this.fileName = fileName;
  53.         this.fileSize = fileSize;
  54.     }

  55.     protected void setFileInfo(String path, String fileName, long fileSize) {
  56.         this.filePath = path;
  57.         this.fileName = fileName;
  58.         this.fileSize = fileSize;
  59.     }

  60.     /**
  61.      * Returns the size of the file being transfered.
  62.      *
  63.      * @return Returns the size of the file being transfered.
  64.      */
  65.     public long getFileSize() {
  66.         return fileSize;
  67.     }

  68.     /**
  69.      * Returns the name of the file being transfered.
  70.      *
  71.      * @return Returns the name of the file being transfered.
  72.      */
  73.     public String getFileName() {
  74.         return fileName;
  75.     }

  76.     /**
  77.      * Returns the local path of the file.
  78.      *
  79.      * @return Returns the local path of the file.
  80.      */
  81.     public String getFilePath() {
  82.         return filePath;
  83.     }

  84.     /**
  85.      * Returns the JID of the peer for this file transfer.
  86.      *
  87.      * @return Returns the JID of the peer for this file transfer.
  88.      */
  89.     public Jid getPeer() {
  90.         return peer;
  91.     }

  92.     /**
  93.      * Returns the progress of the file transfer as a number between 0 and 1.
  94.      *
  95.      * @return Returns the progress of the file transfer as a number between 0
  96.      *         and 1.
  97.      */
  98.     public double getProgress() {
  99.         if (amountWritten <= 0 || fileSize <= 0) {
  100.             return 0;
  101.         }
  102.         return (double) amountWritten / (double) fileSize;
  103.     }

  104.     /**
  105.      * Returns true if the transfer has been cancelled, if it has stopped because
  106.      * of a an error, or the transfer completed successfully.
  107.      *
  108.      * @return Returns true if the transfer has been cancelled, if it has stopped
  109.      *         because of a an error, or the transfer completed successfully.
  110.      */
  111.     public boolean isDone() {
  112.         return status == Status.cancelled || status == Status.error
  113.                 || status == Status.complete || status == Status.refused;
  114.     }

  115.     /**
  116.      * Returns the current status of the file transfer.
  117.      *
  118.      * @return Returns the current status of the file transfer.
  119.      */
  120.     public Status getStatus() {
  121.         return status;
  122.     }

  123.     protected void setError(Error type) {
  124.         this.error = type;
  125.     }

  126.     /**
  127.      * When {@link #getStatus()} returns that there was an {@link Status#error}
  128.      * during the transfer, the type of error can be retrieved through this
  129.      * method.
  130.      *
  131.      * @return Returns the type of error that occurred if one has occurred.
  132.      */
  133.     public Error getError() {
  134.         return error;
  135.     }

  136.     /**
  137.      * If an exception occurs asynchronously it will be stored for later
  138.      * retrieval. If there is an error there maybe an exception set.
  139.      *
  140.      * @return The exception that occurred or null if there was no exception.
  141.      * @see #getError()
  142.      */
  143.     public Exception getException() {
  144.         return exception;
  145.     }

  146.     public String getStreamID() {
  147.         return streamID;
  148.     }

  149.     /**
  150.      * Cancels the file transfer.
  151.      */
  152.     public abstract void cancel();

  153.     protected void setException(Exception exception) {
  154.         this.exception = exception;
  155.     }

  156.     protected void setStatus(Status status) {
  157.         synchronized (statusMonitor) {
  158.             this.status = status;
  159.         }
  160.     }

  161.     protected boolean updateStatus(Status oldStatus, Status newStatus) {
  162.         synchronized (statusMonitor) {
  163.             if (oldStatus != status) {
  164.                 return false;
  165.             }
  166.             status = newStatus;
  167.             return true;
  168.         }
  169.     }

  170.     protected void writeToStream(final InputStream in, final OutputStream out)
  171.                     throws IOException
  172.     {
  173.         final byte[] b = new byte[BUFFER_SIZE];
  174.         int count = 0;
  175.         amountWritten = 0;

  176.         while ((count = in.read(b)) > 0 && !getStatus().equals(Status.cancelled)) {
  177.             out.write(b, 0, count);
  178.             amountWritten += count;
  179.         }

  180.         // the connection was likely terminated abruptly if these are not equal
  181.         if (!getStatus().equals(Status.cancelled) && getError() == Error.none
  182.                 && amountWritten != fileSize) {
  183.             setStatus(Status.error);
  184.             this.error = Error.connection;
  185.         }
  186.     }

  187.     /**
  188.      * A class to represent the current status of the file transfer.
  189.      *
  190.      * @author Alexander Wenckus
  191.      *
  192.      */
  193.     public enum Status {

  194.         /**
  195.          * An error occurred during the transfer.
  196.          *
  197.          * @see FileTransfer#getError()
  198.          */
  199.         error("Error"),

  200.         /**
  201.          * The initial status of the file transfer.
  202.          */
  203.         initial("Initial"),

  204.         /**
  205.          * The file transfer is being negotiated with the peer. The party
  206.          * Receiving the file has the option to accept or refuse a file transfer
  207.          * request. If they accept, then the process of stream negotiation will
  208.          * begin. If they refuse the file will not be transfered.
  209.          *
  210.          * @see #negotiating_stream
  211.          */
  212.         negotiating_transfer("Negotiating Transfer"),

  213.         /**
  214.          * The peer has refused the file transfer request halting the file
  215.          * transfer negotiation process.
  216.          */
  217.         refused("Refused"),

  218.         /**
  219.          * The stream to transfer the file is being negotiated over the chosen
  220.          * stream type. After the stream negotiating process is complete the
  221.          * status becomes negotiated.
  222.          *
  223.          * @see #negotiated
  224.          */
  225.         negotiating_stream("Negotiating Stream"),

  226.         /**
  227.          * After the stream negotiation has completed the intermediate state
  228.          * between the time when the negotiation is finished and the actual
  229.          * transfer begins.
  230.          */
  231.         negotiated("Negotiated"),

  232.         /**
  233.          * The transfer is in progress.
  234.          *
  235.          * @see FileTransfer#getProgress()
  236.          */
  237.         in_progress("In Progress"),

  238.         /**
  239.          * The transfer has completed successfully.
  240.          */
  241.         complete("Complete"),

  242.         /**
  243.          * The file transfer was cancelled
  244.          */
  245.         cancelled("Cancelled");

  246.         private String status;

  247.         private Status(String status) {
  248.             this.status = status;
  249.         }

  250.         public String toString() {
  251.             return status;
  252.         }
  253.     }

  254.     /**
  255.      * Return the length of bytes written out to the stream.
  256.      * @return the amount in bytes written out.
  257.      */
  258.     public long getAmountWritten(){
  259.         return amountWritten;
  260.     }

  261.     public enum Error {
  262.         /**
  263.          * No error
  264.          */
  265.         none("No error"),

  266.         /**
  267.          * The peer did not find any of the provided stream mechanisms
  268.          * acceptable.
  269.          */
  270.         not_acceptable("The peer did not find any of the provided stream mechanisms acceptable."),

  271.         /**
  272.          * The provided file to transfer does not exist or could not be read.
  273.          */
  274.         bad_file("The provided file to transfer does not exist or could not be read."),

  275.         /**
  276.          * The remote user did not respond or the connection timed out.
  277.          */
  278.         no_response("The remote user did not respond or the connection timed out."),

  279.         /**
  280.          * An error occurred over the socket connected to send the file.
  281.          */
  282.         connection("An error occured over the socket connected to send the file."),

  283.         /**
  284.          * An error occurred while sending or receiving the file
  285.          */
  286.         stream("An error occured while sending or recieving the file.");

  287.         private final String msg;

  288.         private Error(String msg) {
  289.             this.msg = msg;
  290.         }

  291.         /**
  292.          * Returns a String representation of this error.
  293.          *
  294.          * @return Returns a String representation of this error.
  295.          */
  296.         public String getMessage() {
  297.             return msg;
  298.         }

  299.         public String toString() {
  300.             return msg;
  301.         }
  302.     }

  303. }