IncomingFileTransfer.java

  1. /**
  2.  *
  3.  * Copyright 2003-2006 Jive Software.
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.filetransfer;

  18. import java.io.File;
  19. import java.io.FileNotFoundException;
  20. import java.io.FileOutputStream;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.io.OutputStream;
  24. import java.util.concurrent.Callable;
  25. import java.util.concurrent.ExecutionException;
  26. import java.util.concurrent.FutureTask;
  27. import java.util.concurrent.TimeUnit;
  28. import java.util.concurrent.TimeoutException;
  29. import java.util.logging.Level;
  30. import java.util.logging.Logger;

  31. import org.jivesoftware.smack.SmackException;
  32. import org.jivesoftware.smack.XMPPException.XMPPErrorException;


  33. /**
  34.  * An incoming file transfer is created when the
  35.  * {@link FileTransferManager#createIncomingFileTransfer(FileTransferRequest)}
  36.  * method is invoked. It is a file being sent to the local user from another
  37.  * user on the jabber network. There are two stages of the file transfer to be
  38.  * concerned with and they can be handled in different ways depending upon the
  39.  * method that is invoked on this class.
  40.  * <p/>
  41.  * The first way that a file is recieved is by calling the
  42.  * {@link #recieveFile()} method. This method, negotiates the appropriate stream
  43.  * method and then returns the <b><i>InputStream</b></i> to read the file
  44.  * data from.
  45.  * <p/>
  46.  * The second way that a file can be recieved through this class is by invoking
  47.  * the {@link #recieveFile(File)} method. This method returns immediatly and
  48.  * takes as its parameter a file on the local file system where the file
  49.  * recieved from the transfer will be put.
  50.  *
  51.  * @author Alexander Wenckus
  52.  */
  53. public class IncomingFileTransfer extends FileTransfer {

  54.     private static final Logger LOGGER = Logger.getLogger(IncomingFileTransfer.class.getName());

  55.     private FileTransferRequest recieveRequest;

  56.     private InputStream inputStream;

  57.     protected IncomingFileTransfer(FileTransferRequest request,
  58.             FileTransferNegotiator transferNegotiator) {
  59.         super(request.getRequestor(), request.getStreamID(), transferNegotiator);
  60.         this.recieveRequest = request;
  61.     }

  62.     /**
  63.      * Negotiates the stream method to transfer the file over and then returns
  64.      * the negotiated stream.
  65.      *
  66.      * @return The negotiated InputStream from which to read the data.
  67.      * @throws SmackException
  68.      * @throws XMPPErrorException If there is an error in the negotiation process an exception
  69.      *                       is thrown.
  70.      * @throws InterruptedException
  71.      */
  72.     public InputStream recieveFile() throws SmackException, XMPPErrorException, InterruptedException {
  73.         if (inputStream != null) {
  74.             throw new IllegalStateException("Transfer already negotiated!");
  75.         }

  76.         try {
  77.             inputStream = negotiateStream();
  78.         }
  79.         catch (XMPPErrorException e) {
  80.             setException(e);
  81.             throw e;
  82.         }

  83.         return inputStream;
  84.     }

  85.     /**
  86.      * This method negotitates the stream and then transfer's the file over the negotiated stream.
  87.      * The transfered file will be saved at the provided location.
  88.      * <p/>
  89.      * This method will return immedialtly, file transfer progress can be monitored through several
  90.      * methods:
  91.      * <p/>
  92.      * <UL>
  93.      * <LI>{@link FileTransfer#getStatus()}
  94.      * <LI>{@link FileTransfer#getProgress()}
  95.      * <LI>{@link FileTransfer#isDone()}
  96.      * </UL>
  97.      *
  98.      * @param file The location to save the file.
  99.      * @throws SmackException when the file transfer fails
  100.      * @throws IOException
  101.      * @throws IllegalArgumentException This exception is thrown when the the provided file is
  102.      *         either null, or cannot be written to.
  103.      */
  104.     public void recieveFile(final File file) throws SmackException, IOException {
  105.         if (file == null) {
  106.             throw new IllegalArgumentException("File cannot be null");
  107.         }
  108.         if (!file.exists()) {
  109.                  file.createNewFile();
  110.             }
  111.         if (!file.canWrite()) {
  112.                 throw new IllegalArgumentException("Cannot write to provided file");
  113.         }

  114.         Thread transferThread = new Thread(new Runnable() {
  115.             public void run() {
  116.                 try {
  117.                     inputStream = negotiateStream();
  118.                 }
  119.                 catch (Exception e) {
  120.                     setStatus(FileTransfer.Status.error);
  121.                     setException(e);
  122.                     return;
  123.                 }

  124.                 OutputStream outputStream = null;
  125.                 try {
  126.                     outputStream = new FileOutputStream(file);
  127.                     setStatus(Status.in_progress);
  128.                     writeToStream(inputStream, outputStream);
  129.                 }
  130.                 catch (FileNotFoundException e) {
  131.                     setStatus(Status.error);
  132.                     setError(Error.bad_file);
  133.                     setException(e);
  134.                 }
  135.                 catch (IOException e) {
  136.                     setStatus(Status.error);
  137.                     setError(Error.stream);
  138.                     setException(e);
  139.                 }


  140.                 if (getStatus().equals(Status.in_progress)) {
  141.                     setStatus(Status.complete);
  142.                 }
  143.                 if (inputStream != null) {
  144.                     try {
  145.                         inputStream.close();
  146.                     } catch (IOException e) {
  147.                         LOGGER.log(Level.WARNING, "Closing input stream", e);
  148.                     }
  149.                 }
  150.                 if (outputStream != null) {
  151.                     try {
  152.                         outputStream.close();
  153.                     } catch (IOException e) {
  154.                         LOGGER.log(Level.WARNING, "Closing output stream", e);
  155.                     }
  156.                 }
  157.             }
  158.         }, "File Transfer " + streamID);
  159.         transferThread.start();
  160.     }

  161.     private InputStream negotiateStream() throws SmackException, XMPPErrorException, InterruptedException {
  162.         setStatus(Status.negotiating_transfer);
  163.         final StreamNegotiator streamNegotiator = negotiator
  164.                 .selectStreamNegotiator(recieveRequest);
  165.         setStatus(Status.negotiating_stream);
  166.         FutureTask<InputStream> streamNegotiatorTask = new FutureTask<InputStream>(
  167.                 new Callable<InputStream>() {

  168.                     public InputStream call() throws Exception {
  169.                         return streamNegotiator
  170.                                 .createIncomingStream(recieveRequest.getStreamInitiation());
  171.                     }
  172.                 });
  173.         streamNegotiatorTask.run();
  174.         InputStream inputStream;
  175.         try {
  176.             inputStream = streamNegotiatorTask.get(15, TimeUnit.SECONDS);
  177.         }
  178.         catch (InterruptedException e) {
  179.             throw new SmackException("Interruption while executing", e);
  180.         }
  181.         catch (ExecutionException e) {
  182.             throw new SmackException("Error in execution", e);
  183.         }
  184.         catch (TimeoutException e) {
  185.             throw new SmackException("Request timed out", e);
  186.         }
  187.         finally {
  188.             streamNegotiatorTask.cancel(true);
  189.         }
  190.         setStatus(Status.negotiated);
  191.         return inputStream;
  192.     }

  193.     public void cancel() {
  194.         setStatus(Status.cancelled);
  195.     }

  196. }