• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.*;
25 import 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  */
47 public class IncomingFileTransfer extends FileTransfer {
48 
49     private FileTransferRequest recieveRequest;
50 
51     private InputStream inputStream;
52 
IncomingFileTransfer(FileTransferRequest request, FileTransferNegotiator transferNegotiator)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      */
recieveFile()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      */
recieveFile(final File file)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 
handleXMPPException(XMPPException e)172     private void handleXMPPException(XMPPException e) {
173         setStatus(FileTransfer.Status.error);
174         setException(e);
175     }
176 
negotiateStream()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 
cancel()211     public void cancel() {
212         setStatus(Status.cancelled);
213     }
214 
215 }
216