Home | History | Annotate | Download | only in filetransfer
      1 /**
      2  * $RCSfile$
      3  * $Revision$
      4  * $Date$
      5  *
      6  * Copyright 2003-2006 Jive Software.
      7  *
      8  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
      9  * you may not use this file except in compliance with the License.
     10  * You may obtain a copy of the License at
     11  *
     12  *     http://www.apache.org/licenses/LICENSE-2.0
     13  *
     14  * Unless required by applicable law or agreed to in writing, software
     15  * distributed under the License is distributed on an "AS IS" BASIS,
     16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     17  * See the License for the specific language governing permissions and
     18  * limitations under the License.
     19  */
     20 package org.jivesoftware.smackx.filetransfer;
     21 
     22 import org.jivesoftware.smack.XMPPException;
     23 
     24 import java.io.IOException;
     25 import java.io.InputStream;
     26 import java.io.OutputStream;
     27 
     28 /**
     29  * Contains the generic file information and progress related to a particular
     30  * file transfer.
     31  *
     32  * @author Alexander Wenckus
     33  *
     34  */
     35 public abstract class FileTransfer {
     36 
     37 	private String fileName;
     38 
     39 	private String filePath;
     40 
     41 	private long fileSize;
     42 
     43 	private String peer;
     44 
     45 	private Status status = Status.initial;
     46 
     47     private final Object statusMonitor = new Object();
     48 
     49 	protected FileTransferNegotiator negotiator;
     50 
     51 	protected String streamID;
     52 
     53 	protected long amountWritten = -1;
     54 
     55 	private Error error;
     56 
     57 	private Exception exception;
     58 
     59     /**
     60      * Buffer size between input and output
     61      */
     62     private static final int BUFFER_SIZE = 8192;
     63 
     64     protected FileTransfer(String peer, String streamID,
     65 			FileTransferNegotiator negotiator) {
     66 		this.peer = peer;
     67 		this.streamID = streamID;
     68 		this.negotiator = negotiator;
     69 	}
     70 
     71 	protected void setFileInfo(String fileName, long fileSize) {
     72 		this.fileName = fileName;
     73 		this.fileSize = fileSize;
     74 	}
     75 
     76 	protected void setFileInfo(String path, String fileName, long fileSize) {
     77 		this.filePath = path;
     78 		this.fileName = fileName;
     79 		this.fileSize = fileSize;
     80 	}
     81 
     82 	/**
     83 	 * Returns the size of the file being transfered.
     84 	 *
     85 	 * @return Returns the size of the file being transfered.
     86 	 */
     87 	public long getFileSize() {
     88 		return fileSize;
     89 	}
     90 
     91 	/**
     92 	 * Returns the name of the file being transfered.
     93 	 *
     94 	 * @return Returns the name of the file being transfered.
     95 	 */
     96 	public String getFileName() {
     97 		return fileName;
     98 	}
     99 
    100 	/**
    101 	 * Returns the local path of the file.
    102 	 *
    103 	 * @return Returns the local path of the file.
    104 	 */
    105 	public String getFilePath() {
    106 		return filePath;
    107 	}
    108 
    109 	/**
    110 	 * Returns the JID of the peer for this file transfer.
    111 	 *
    112 	 * @return Returns the JID of the peer for this file transfer.
    113 	 */
    114 	public String getPeer() {
    115 		return peer;
    116 	}
    117 
    118 	/**
    119 	 * Returns the progress of the file transfer as a number between 0 and 1.
    120 	 *
    121 	 * @return Returns the progress of the file transfer as a number between 0
    122 	 *         and 1.
    123 	 */
    124 	public double getProgress() {
    125         if (amountWritten <= 0 || fileSize <= 0) {
    126             return 0;
    127         }
    128         return (double) amountWritten / (double) fileSize;
    129 	}
    130 
    131 	/**
    132 	 * Returns true if the transfer has been cancelled, if it has stopped because
    133 	 * of a an error, or the transfer completed successfully.
    134 	 *
    135 	 * @return Returns true if the transfer has been cancelled, if it has stopped
    136 	 *         because of a an error, or the transfer completed successfully.
    137 	 */
    138 	public boolean isDone() {
    139 		return status == Status.cancelled || status == Status.error
    140 				|| status == Status.complete || status == Status.refused;
    141 	}
    142 
    143 	/**
    144 	 * Returns the current status of the file transfer.
    145 	 *
    146 	 * @return Returns the current status of the file transfer.
    147 	 */
    148 	public Status getStatus() {
    149 		return status;
    150 	}
    151 
    152 	protected void setError(Error type) {
    153 		this.error = type;
    154 	}
    155 
    156 	/**
    157 	 * When {@link #getStatus()} returns that there was an {@link Status#error}
    158 	 * during the transfer, the type of error can be retrieved through this
    159 	 * method.
    160 	 *
    161 	 * @return Returns the type of error that occurred if one has occurred.
    162 	 */
    163 	public Error getError() {
    164 		return error;
    165 	}
    166 
    167 	/**
    168 	 * If an exception occurs asynchronously it will be stored for later
    169 	 * retrieval. If there is an error there maybe an exception set.
    170 	 *
    171 	 * @return The exception that occurred or null if there was no exception.
    172 	 * @see #getError()
    173 	 */
    174 	public Exception getException() {
    175 		return exception;
    176 	}
    177 
    178     public String getStreamID() {
    179         return streamID;
    180     }
    181 
    182 	/**
    183 	 * Cancels the file transfer.
    184 	 */
    185 	public abstract void cancel();
    186 
    187 	protected void setException(Exception exception) {
    188 		this.exception = exception;
    189 	}
    190 
    191 	protected void setStatus(Status status) {
    192         synchronized (statusMonitor) {
    193 		    this.status = status;
    194 	    }
    195     }
    196 
    197     protected boolean updateStatus(Status oldStatus, Status newStatus) {
    198         synchronized (statusMonitor) {
    199             if (oldStatus != status) {
    200                 return false;
    201             }
    202             status = newStatus;
    203             return true;
    204         }
    205     }
    206 
    207 	protected void writeToStream(final InputStream in, final OutputStream out)
    208 			throws XMPPException
    209     {
    210 		final byte[] b = new byte[BUFFER_SIZE];
    211 		int count = 0;
    212 		amountWritten = 0;
    213 
    214         do {
    215 			// write to the output stream
    216 			try {
    217 				out.write(b, 0, count);
    218 			} catch (IOException e) {
    219 				throw new XMPPException("error writing to output stream", e);
    220 			}
    221 
    222 			amountWritten += count;
    223 
    224 			// read more bytes from the input stream
    225 			try {
    226 				count = in.read(b);
    227 			} catch (IOException e) {
    228 				throw new XMPPException("error reading from input stream", e);
    229 			}
    230 		} while (count != -1 && !getStatus().equals(Status.cancelled));
    231 
    232 		// the connection was likely terminated abrubtly if these are not equal
    233 		if (!getStatus().equals(Status.cancelled) && getError() == Error.none
    234 				&& amountWritten != fileSize) {
    235             setStatus(Status.error);
    236 			this.error = Error.connection;
    237 		}
    238 	}
    239 
    240 	/**
    241 	 * A class to represent the current status of the file transfer.
    242 	 *
    243 	 * @author Alexander Wenckus
    244 	 *
    245 	 */
    246 	public enum Status {
    247 
    248 		/**
    249 		 * An error occurred during the transfer.
    250 		 *
    251 		 * @see FileTransfer#getError()
    252 		 */
    253 		error("Error"),
    254 
    255 		/**
    256          * The initial status of the file transfer.
    257          */
    258         initial("Initial"),
    259 
    260         /**
    261 		 * The file transfer is being negotiated with the peer. The party
    262 		 * Receiving the file has the option to accept or refuse a file transfer
    263 		 * request. If they accept, then the process of stream negotiation will
    264 		 * begin. If they refuse the file will not be transfered.
    265 		 *
    266 		 * @see #negotiating_stream
    267 		 */
    268 		negotiating_transfer("Negotiating Transfer"),
    269 
    270 		/**
    271 		 * The peer has refused the file transfer request halting the file
    272 		 * transfer negotiation process.
    273 		 */
    274 		refused("Refused"),
    275 
    276 		/**
    277 		 * The stream to transfer the file is being negotiated over the chosen
    278 		 * stream type. After the stream negotiating process is complete the
    279 		 * status becomes negotiated.
    280 		 *
    281 		 * @see #negotiated
    282 		 */
    283 		negotiating_stream("Negotiating Stream"),
    284 
    285 		/**
    286 		 * After the stream negotiation has completed the intermediate state
    287 		 * between the time when the negotiation is finished and the actual
    288 		 * transfer begins.
    289 		 */
    290 		negotiated("Negotiated"),
    291 
    292 		/**
    293 		 * The transfer is in progress.
    294 		 *
    295 		 * @see FileTransfer#getProgress()
    296 		 */
    297 		in_progress("In Progress"),
    298 
    299 		/**
    300 		 * The transfer has completed successfully.
    301 		 */
    302 		complete("Complete"),
    303 
    304 		/**
    305 		 * The file transfer was cancelled
    306 		 */
    307 		cancelled("Cancelled");
    308 
    309         private String status;
    310 
    311         private Status(String status) {
    312             this.status = status;
    313         }
    314 
    315         public String toString() {
    316             return status;
    317         }
    318     }
    319 
    320     /**
    321      * Return the length of bytes written out to the stream.
    322      * @return the amount in bytes written out.
    323      */
    324     public long getAmountWritten(){
    325         return amountWritten;
    326     }
    327 
    328     public enum Error {
    329 		/**
    330 		 * No error
    331 		 */
    332 		none("No error"),
    333 
    334 		/**
    335 		 * The peer did not find any of the provided stream mechanisms
    336 		 * acceptable.
    337 		 */
    338 		not_acceptable("The peer did not find any of the provided stream mechanisms acceptable."),
    339 
    340 		/**
    341 		 * The provided file to transfer does not exist or could not be read.
    342 		 */
    343 		bad_file("The provided file to transfer does not exist or could not be read."),
    344 
    345 		/**
    346 		 * The remote user did not respond or the connection timed out.
    347 		 */
    348 		no_response("The remote user did not respond or the connection timed out."),
    349 
    350 		/**
    351 		 * An error occurred over the socket connected to send the file.
    352 		 */
    353 		connection("An error occured over the socket connected to send the file."),
    354 
    355 		/**
    356 		 * An error occurred while sending or receiving the file
    357 		 */
    358 		stream("An error occured while sending or recieving the file.");
    359 
    360 		private final String msg;
    361 
    362 		private Error(String msg) {
    363 			this.msg = msg;
    364 		}
    365 
    366 		/**
    367 		 * Returns a String representation of this error.
    368 		 *
    369 		 * @return Returns a String representation of this error.
    370 		 */
    371 		public String getMessage() {
    372 			return msg;
    373 		}
    374 
    375 		public String toString() {
    376 			return msg;
    377 		}
    378 	}
    379 
    380 }
    381