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