blob: b840fd537f9813929a95210c02cfbcfef35378e1 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/**
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 */
20package org.jivesoftware.smackx.filetransfer;
21
22import org.jivesoftware.smack.XMPPException;
23
24import java.io.IOException;
25import java.io.InputStream;
26import 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 */
35public 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}