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.*;
     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 
     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 }
    216