001/** 002 * 003 * Copyright 2003-2006 Jive Software. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.filetransfer; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.io.OutputStream; 022 023/** 024 * Contains the generic file information and progress related to a particular 025 * file transfer. 026 * 027 * @author Alexander Wenckus 028 * 029 */ 030public abstract class FileTransfer { 031 032 private String fileName; 033 034 private String filePath; 035 036 private long fileSize; 037 038 private String peer; 039 040 private Status status = Status.initial; 041 042 private final Object statusMonitor = new Object(); 043 044 protected FileTransferNegotiator negotiator; 045 046 protected String streamID; 047 048 protected long amountWritten = -1; 049 050 private Error error; 051 052 private Exception exception; 053 054 /** 055 * Buffer size between input and output 056 */ 057 private static final int BUFFER_SIZE = 8192; 058 059 protected FileTransfer(String peer, String streamID, 060 FileTransferNegotiator negotiator) { 061 this.peer = peer; 062 this.streamID = streamID; 063 this.negotiator = negotiator; 064 } 065 066 protected void setFileInfo(String fileName, long fileSize) { 067 this.fileName = fileName; 068 this.fileSize = fileSize; 069 } 070 071 protected void setFileInfo(String path, String fileName, long fileSize) { 072 this.filePath = path; 073 this.fileName = fileName; 074 this.fileSize = fileSize; 075 } 076 077 /** 078 * Returns the size of the file being transfered. 079 * 080 * @return Returns the size of the file being transfered. 081 */ 082 public long getFileSize() { 083 return fileSize; 084 } 085 086 /** 087 * Returns the name of the file being transfered. 088 * 089 * @return Returns the name of the file being transfered. 090 */ 091 public String getFileName() { 092 return fileName; 093 } 094 095 /** 096 * Returns the local path of the file. 097 * 098 * @return Returns the local path of the file. 099 */ 100 public String getFilePath() { 101 return filePath; 102 } 103 104 /** 105 * Returns the JID of the peer for this file transfer. 106 * 107 * @return Returns the JID of the peer for this file transfer. 108 */ 109 public String getPeer() { 110 return peer; 111 } 112 113 /** 114 * Returns the progress of the file transfer as a number between 0 and 1. 115 * 116 * @return Returns the progress of the file transfer as a number between 0 117 * and 1. 118 */ 119 public double getProgress() { 120 if (amountWritten <= 0 || fileSize <= 0) { 121 return 0; 122 } 123 return (double) amountWritten / (double) fileSize; 124 } 125 126 /** 127 * Returns true if the transfer has been cancelled, if it has stopped because 128 * of a an error, or the transfer completed successfully. 129 * 130 * @return Returns true if the transfer has been cancelled, if it has stopped 131 * because of a an error, or the transfer completed successfully. 132 */ 133 public boolean isDone() { 134 return status == Status.cancelled || status == Status.error 135 || status == Status.complete || status == Status.refused; 136 } 137 138 /** 139 * Returns the current status of the file transfer. 140 * 141 * @return Returns the current status of the file transfer. 142 */ 143 public Status getStatus() { 144 return status; 145 } 146 147 protected void setError(Error type) { 148 this.error = type; 149 } 150 151 /** 152 * When {@link #getStatus()} returns that there was an {@link Status#error} 153 * during the transfer, the type of error can be retrieved through this 154 * method. 155 * 156 * @return Returns the type of error that occurred if one has occurred. 157 */ 158 public Error getError() { 159 return error; 160 } 161 162 /** 163 * If an exception occurs asynchronously it will be stored for later 164 * retrieval. If there is an error there maybe an exception set. 165 * 166 * @return The exception that occurred or null if there was no exception. 167 * @see #getError() 168 */ 169 public Exception getException() { 170 return exception; 171 } 172 173 public String getStreamID() { 174 return streamID; 175 } 176 177 /** 178 * Cancels the file transfer. 179 */ 180 public abstract void cancel(); 181 182 protected void setException(Exception exception) { 183 this.exception = exception; 184 } 185 186 protected void setStatus(Status status) { 187 synchronized (statusMonitor) { 188 this.status = status; 189 } 190 } 191 192 protected boolean updateStatus(Status oldStatus, Status newStatus) { 193 synchronized (statusMonitor) { 194 if (oldStatus != status) { 195 return false; 196 } 197 status = newStatus; 198 return true; 199 } 200 } 201 202 protected void writeToStream(final InputStream in, final OutputStream out) 203 throws IOException 204 { 205 final byte[] b = new byte[BUFFER_SIZE]; 206 int count = 0; 207 amountWritten = 0; 208 209 while ((count = in.read(b)) > 0 && !getStatus().equals(Status.cancelled)) { 210 out.write(b, 0, count); 211 amountWritten += count; 212 } 213 214 // the connection was likely terminated abruptly if these are not equal 215 if (!getStatus().equals(Status.cancelled) && getError() == Error.none 216 && amountWritten != fileSize) { 217 setStatus(Status.error); 218 this.error = Error.connection; 219 } 220 } 221 222 /** 223 * A class to represent the current status of the file transfer. 224 * 225 * @author Alexander Wenckus 226 * 227 */ 228 public enum Status { 229 230 /** 231 * An error occurred during the transfer. 232 * 233 * @see FileTransfer#getError() 234 */ 235 error("Error"), 236 237 /** 238 * The initial status of the file transfer. 239 */ 240 initial("Initial"), 241 242 /** 243 * The file transfer is being negotiated with the peer. The party 244 * Receiving the file has the option to accept or refuse a file transfer 245 * request. If they accept, then the process of stream negotiation will 246 * begin. If they refuse the file will not be transfered. 247 * 248 * @see #negotiating_stream 249 */ 250 negotiating_transfer("Negotiating Transfer"), 251 252 /** 253 * The peer has refused the file transfer request halting the file 254 * transfer negotiation process. 255 */ 256 refused("Refused"), 257 258 /** 259 * The stream to transfer the file is being negotiated over the chosen 260 * stream type. After the stream negotiating process is complete the 261 * status becomes negotiated. 262 * 263 * @see #negotiated 264 */ 265 negotiating_stream("Negotiating Stream"), 266 267 /** 268 * After the stream negotiation has completed the intermediate state 269 * between the time when the negotiation is finished and the actual 270 * transfer begins. 271 */ 272 negotiated("Negotiated"), 273 274 /** 275 * The transfer is in progress. 276 * 277 * @see FileTransfer#getProgress() 278 */ 279 in_progress("In Progress"), 280 281 /** 282 * The transfer has completed successfully. 283 */ 284 complete("Complete"), 285 286 /** 287 * The file transfer was cancelled 288 */ 289 cancelled("Cancelled"); 290 291 private String status; 292 293 private Status(String status) { 294 this.status = status; 295 } 296 297 public String toString() { 298 return status; 299 } 300 } 301 302 /** 303 * Return the length of bytes written out to the stream. 304 * @return the amount in bytes written out. 305 */ 306 public long getAmountWritten(){ 307 return amountWritten; 308 } 309 310 public enum Error { 311 /** 312 * No error 313 */ 314 none("No error"), 315 316 /** 317 * The peer did not find any of the provided stream mechanisms 318 * acceptable. 319 */ 320 not_acceptable("The peer did not find any of the provided stream mechanisms acceptable."), 321 322 /** 323 * The provided file to transfer does not exist or could not be read. 324 */ 325 bad_file("The provided file to transfer does not exist or could not be read."), 326 327 /** 328 * The remote user did not respond or the connection timed out. 329 */ 330 no_response("The remote user did not respond or the connection timed out."), 331 332 /** 333 * An error occurred over the socket connected to send the file. 334 */ 335 connection("An error occured over the socket connected to send the file."), 336 337 /** 338 * An error occurred while sending or receiving the file 339 */ 340 stream("An error occured while sending or recieving the file."); 341 342 private final String msg; 343 344 private Error(String msg) { 345 this.msg = msg; 346 } 347 348 /** 349 * Returns a String representation of this error. 350 * 351 * @return Returns a String representation of this error. 352 */ 353 public String getMessage() { 354 return msg; 355 } 356 357 public String toString() { 358 return msg; 359 } 360 } 361 362}