001/** 002 * 003 * Copyright 2003-2006 Jive Software. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.filetransfer; 018 019import org.jivesoftware.smack.SmackException; 020import org.jivesoftware.smack.XMPPException.XMPPErrorException; 021 022import java.io.*; 023import java.util.concurrent.*; 024 025/** 026 * An incoming file transfer is created when the 027 * {@link FileTransferManager#createIncomingFileTransfer(FileTransferRequest)} 028 * method is invoked. It is a file being sent to the local user from another 029 * user on the jabber network. There are two stages of the file transfer to be 030 * concerned with and they can be handled in different ways depending upon the 031 * method that is invoked on this class. 032 * <p/> 033 * The first way that a file is recieved is by calling the 034 * {@link #recieveFile()} method. This method, negotiates the appropriate stream 035 * method and then returns the <b><i>InputStream</b></i> to read the file 036 * data from. 037 * <p/> 038 * The second way that a file can be recieved through this class is by invoking 039 * the {@link #recieveFile(File)} method. This method returns immediatly and 040 * takes as its parameter a file on the local file system where the file 041 * recieved from the transfer will be put. 042 * 043 * @author Alexander Wenckus 044 */ 045public class IncomingFileTransfer extends FileTransfer { 046 047 private FileTransferRequest recieveRequest; 048 049 private InputStream inputStream; 050 051 protected IncomingFileTransfer(FileTransferRequest request, 052 FileTransferNegotiator transferNegotiator) { 053 super(request.getRequestor(), request.getStreamID(), transferNegotiator); 054 this.recieveRequest = request; 055 } 056 057 /** 058 * Negotiates the stream method to transfer the file over and then returns 059 * the negotiated stream. 060 * 061 * @return The negotiated InputStream from which to read the data. 062 * @throws SmackException 063 * @throws XMPPErrorException If there is an error in the negotiation process an exception 064 * is thrown. 065 */ 066 public InputStream recieveFile() throws SmackException, XMPPErrorException { 067 if (inputStream != null) { 068 throw new IllegalStateException("Transfer already negotiated!"); 069 } 070 071 try { 072 inputStream = negotiateStream(); 073 } 074 catch (XMPPErrorException e) { 075 setException(e); 076 throw e; 077 } 078 079 return inputStream; 080 } 081 082 /** 083 * This method negotitates the stream and then transfer's the file over the negotiated stream. 084 * The transfered file will be saved at the provided location. 085 * <p/> 086 * This method will return immedialtly, file transfer progress can be monitored through several 087 * methods: 088 * <p/> 089 * <UL> 090 * <LI>{@link FileTransfer#getStatus()} 091 * <LI>{@link FileTransfer#getProgress()} 092 * <LI>{@link FileTransfer#isDone()} 093 * </UL> 094 * 095 * @param file The location to save the file. 096 * @throws SmackException when the file transfer fails 097 * @throws IllegalArgumentException This exception is thrown when the the provided file is 098 * either null, or cannot be written to. 099 */ 100 public void recieveFile(final File file) throws SmackException { 101 if (file != null) { 102 if (!file.exists()) { 103 try { 104 file.createNewFile(); 105 } 106 catch (IOException e) { 107 throw new SmackException( 108 "Could not create file to write too", e); 109 } 110 } 111 if (!file.canWrite()) { 112 throw new IllegalArgumentException("Cannot write to provided file"); 113 } 114 } 115 else { 116 throw new IllegalArgumentException("File cannot be null"); 117 } 118 119 Thread transferThread = new Thread(new Runnable() { 120 public void run() { 121 try { 122 inputStream = negotiateStream(); 123 } 124 catch (Exception e) { 125 setStatus(FileTransfer.Status.error); 126 setException(e); 127 return; 128 } 129 130 OutputStream outputStream = null; 131 try { 132 outputStream = new FileOutputStream(file); 133 setStatus(Status.in_progress); 134 writeToStream(inputStream, outputStream); 135 } 136 catch (SmackException e) { 137 setStatus(Status.error); 138 setError(Error.stream); 139 setException(e); 140 } 141 catch (FileNotFoundException e) { 142 setStatus(Status.error); 143 setError(Error.bad_file); 144 setException(e); 145 } 146 147 if (getStatus().equals(Status.in_progress)) { 148 setStatus(Status.complete); 149 } 150 if (inputStream != null) { 151 try { 152 inputStream.close(); 153 } 154 catch (Throwable io) { 155 /* Ignore */ 156 } 157 } 158 if (outputStream != null) { 159 try { 160 outputStream.close(); 161 } 162 catch (Throwable io) { 163 /* Ignore */ 164 } 165 } 166 } 167 }, "File Transfer " + streamID); 168 transferThread.start(); 169 } 170 171 private InputStream negotiateStream() throws SmackException, XMPPErrorException { 172 setStatus(Status.negotiating_transfer); 173 final StreamNegotiator streamNegotiator = negotiator 174 .selectStreamNegotiator(recieveRequest); 175 setStatus(Status.negotiating_stream); 176 FutureTask<InputStream> streamNegotiatorTask = new FutureTask<InputStream>( 177 new Callable<InputStream>() { 178 179 public InputStream call() throws Exception { 180 return streamNegotiator 181 .createIncomingStream(recieveRequest.getStreamInitiation()); 182 } 183 }); 184 streamNegotiatorTask.run(); 185 InputStream inputStream; 186 try { 187 inputStream = streamNegotiatorTask.get(15, TimeUnit.SECONDS); 188 } 189 catch (InterruptedException e) { 190 throw new SmackException("Interruption while executing", e); 191 } 192 catch (ExecutionException e) { 193 throw new SmackException("Error in execution", e); 194 } 195 catch (TimeoutException e) { 196 throw new SmackException("Request timed out", e); 197 } 198 finally { 199 streamNegotiatorTask.cancel(true); 200 } 201 setStatus(Status.negotiated); 202 return inputStream; 203 } 204 205 public void cancel() { 206 setStatus(Status.cancelled); 207 } 208 209}