blob: 91a5a0d8354467977a65a024933a1469343ca839 [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.*;
25import java.util.concurrent.*;
26
27/**
28 * An incoming file transfer is created when the
29 * {@link FileTransferManager#createIncomingFileTransfer(FileTransferRequest)}
30 * method is invoked. It is a file being sent to the local user from another
31 * user on the jabber network. There are two stages of the file transfer to be
32 * concerned with and they can be handled in different ways depending upon the
33 * method that is invoked on this class.
34 * <p/>
35 * The first way that a file is recieved is by calling the
36 * {@link #recieveFile()} method. This method, negotiates the appropriate stream
37 * method and then returns the <b><i>InputStream</b></i> to read the file
38 * data from.
39 * <p/>
40 * The second way that a file can be recieved through this class is by invoking
41 * the {@link #recieveFile(File)} method. This method returns immediatly and
42 * takes as its parameter a file on the local file system where the file
43 * recieved from the transfer will be put.
44 *
45 * @author Alexander Wenckus
46 */
47public class IncomingFileTransfer extends FileTransfer {
48
49 private FileTransferRequest recieveRequest;
50
51 private InputStream inputStream;
52
53 protected IncomingFileTransfer(FileTransferRequest request,
54 FileTransferNegotiator transferNegotiator) {
55 super(request.getRequestor(), request.getStreamID(), transferNegotiator);
56 this.recieveRequest = request;
57 }
58
59 /**
60 * Negotiates the stream method to transfer the file over and then returns
61 * the negotiated stream.
62 *
63 * @return The negotiated InputStream from which to read the data.
64 * @throws XMPPException If there is an error in the negotiation process an exception
65 * is thrown.
66 */
67 public InputStream recieveFile() throws XMPPException {
68 if (inputStream != null) {
69 throw new IllegalStateException("Transfer already negotiated!");
70 }
71
72 try {
73 inputStream = negotiateStream();
74 }
75 catch (XMPPException e) {
76 setException(e);
77 throw e;
78 }
79
80 return inputStream;
81 }
82
83 /**
84 * This method negotitates the stream and then transfer's the file over the
85 * negotiated stream. The transfered file will be saved at the provided
86 * location.
87 * <p/>
88 * This method will return immedialtly, file transfer progress can be
89 * monitored through several methods:
90 * <p/>
91 * <UL>
92 * <LI>{@link FileTransfer#getStatus()}
93 * <LI>{@link FileTransfer#getProgress()}
94 * <LI>{@link FileTransfer#isDone()}
95 * </UL>
96 *
97 * @param file The location to save the file.
98 * @throws XMPPException when the file transfer fails
99 * @throws IllegalArgumentException This exception is thrown when the the provided file is
100 * either null, or cannot be written to.
101 */
102 public void recieveFile(final File file) throws XMPPException {
103 if (file != null) {
104 if (!file.exists()) {
105 try {
106 file.createNewFile();
107 }
108 catch (IOException e) {
109 throw new XMPPException(
110 "Could not create file to write too", e);
111 }
112 }
113 if (!file.canWrite()) {
114 throw new IllegalArgumentException("Cannot write to provided file");
115 }
116 }
117 else {
118 throw new IllegalArgumentException("File cannot be null");
119 }
120
121 Thread transferThread = new Thread(new Runnable() {
122 public void run() {
123 try {
124 inputStream = negotiateStream();
125 }
126 catch (XMPPException e) {
127 handleXMPPException(e);
128 return;
129 }
130
131 OutputStream outputStream = null;
132 try {
133 outputStream = new FileOutputStream(file);
134 setStatus(Status.in_progress);
135 writeToStream(inputStream, outputStream);
136 }
137 catch (XMPPException e) {
138 setStatus(Status.error);
139 setError(Error.stream);
140 setException(e);
141 }
142 catch (FileNotFoundException e) {
143 setStatus(Status.error);
144 setError(Error.bad_file);
145 setException(e);
146 }
147
148 if (getStatus().equals(Status.in_progress)) {
149 setStatus(Status.complete);
150 }
151 if (inputStream != null) {
152 try {
153 inputStream.close();
154 }
155 catch (Throwable io) {
156 /* Ignore */
157 }
158 }
159 if (outputStream != null) {
160 try {
161 outputStream.close();
162 }
163 catch (Throwable io) {
164 /* Ignore */
165 }
166 }
167 }
168 }, "File Transfer " + streamID);
169 transferThread.start();
170 }
171
172 private void handleXMPPException(XMPPException e) {
173 setStatus(FileTransfer.Status.error);
174 setException(e);
175 }
176
177 private InputStream negotiateStream() throws XMPPException {
178 setStatus(Status.negotiating_transfer);
179 final StreamNegotiator streamNegotiator = negotiator
180 .selectStreamNegotiator(recieveRequest);
181 setStatus(Status.negotiating_stream);
182 FutureTask<InputStream> streamNegotiatorTask = new FutureTask<InputStream>(
183 new Callable<InputStream>() {
184
185 public InputStream call() throws Exception {
186 return streamNegotiator
187 .createIncomingStream(recieveRequest.getStreamInitiation());
188 }
189 });
190 streamNegotiatorTask.run();
191 InputStream inputStream;
192 try {
193 inputStream = streamNegotiatorTask.get(15, TimeUnit.SECONDS);
194 }
195 catch (InterruptedException e) {
196 throw new XMPPException("Interruption while executing", e);
197 }
198 catch (ExecutionException e) {
199 throw new XMPPException("Error in execution", e);
200 }
201 catch (TimeoutException e) {
202 throw new XMPPException("Request timed out", e);
203 }
204 finally {
205 streamNegotiatorTask.cancel(true);
206 }
207 setStatus(Status.negotiated);
208 return inputStream;
209 }
210
211 public void cancel() {
212 setStatus(Status.cancelled);
213 }
214
215}