KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > net > tftp > TFTPClient


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

16 package org.apache.commons.net.tftp;
17
18 import java.io.IOException JavaDoc;
19 import java.io.InputStream JavaDoc;
20 import java.io.InterruptedIOException JavaDoc;
21 import java.io.OutputStream JavaDoc;
22 import java.net.InetAddress JavaDoc;
23 import java.net.SocketException JavaDoc;
24 import java.net.UnknownHostException JavaDoc;
25 import org.apache.commons.net.io.FromNetASCIIOutputStream;
26 import org.apache.commons.net.io.ToNetASCIIInputStream;
27
28 /***
29  * The TFTPClient class encapsulates all the aspects of the TFTP protocol
30  * necessary to receive and send files through TFTP. It is derived from
31  * the {@link org.apache.commons.net.tftp.TFTP} because
32  * it is more convenient than using aggregation, and as a result exposes
33  * the same set of methods to allow you to deal with the TFTP protocol
34  * directly. However, almost every user should only be concerend with the
35  * the {@link org.apache.commons.net.DatagramSocketClient#open open() },
36  * {@link org.apache.commons.net.DatagramSocketClient#close close() },
37  * {@link #sendFile sendFile() }, and
38  * {@link #receiveFile receiveFile() } methods. Additionally, the
39  * {@link #setMaxTimeouts setMaxTimeouts() } and
40  * {@link org.apache.commons.net.DatagramSocketClient#setDefaultTimeout setDefaultTimeout() }
41  * methods may be of importance for performance
42  * tuning.
43  * <p>
44  * Details regarding the TFTP protocol and the format of TFTP packets can
45  * be found in RFC 783. But the point of these classes is to keep you
46  * from having to worry about the internals.
47  * <p>
48  * <p>
49  * @author Daniel F. Savarese
50  * @see TFTP
51  * @see TFTPPacket
52  * @see TFTPPacketException
53  ***/

54
55 public class TFTPClient extends TFTP
56 {
57     /***
58      * The default number of times a receive attempt is allowed to timeout
59      * before ending attempts to retry the receive and failing. The default
60      * is 5 timeouts.
61      ***/

62     public static final int DEFAULT_MAX_TIMEOUTS = 5;
63
64     /*** The maximum number of timeouts allowed before failing. ***/
65     private int __maxTimeouts;
66
67     /***
68      * Creates a TFTPClient instance with a default timeout of DEFAULT_TIMEOUT,
69      * maximum timeouts value of DEFAULT_MAX_TIMEOUTS, a null socket,
70      * and buffered operations disabled.
71      ***/

72     public TFTPClient()
73     {
74         __maxTimeouts = DEFAULT_MAX_TIMEOUTS;
75     }
76
77     /***
78      * Sets the maximum number of times a receive attempt is allowed to
79      * timeout during a receiveFile() or sendFile() operation before ending
80      * attempts to retry the receive and failing.
81      * The default is DEFAULT_MAX_TIMEOUTS.
82      * <p>
83      * @param numTimeouts The maximum number of timeouts to allow. Values
84      * less than 1 should not be used, but if they are, they are
85      * treated as 1.
86      ***/

87     public void setMaxTimeouts(int numTimeouts)
88     {
89         if (numTimeouts < 1)
90             __maxTimeouts = 1;
91         else
92             __maxTimeouts = numTimeouts;
93     }
94
95     /***
96      * Returns the maximum number of times a receive attempt is allowed to
97      * timeout before ending attempts to retry the receive and failing.
98      * <p>
99      * @return The maximum number of timeouts allowed.
100      ***/

101     public int getMaxTimeouts()
102     {
103         return __maxTimeouts;
104     }
105
106
107     /***
108      * Requests a named file from a remote host, writes the
109      * file to an OutputStream, closes the connection, and returns the number
110      * of bytes read. A local UDP socket must first be created by
111      * {@link org.apache.commons.net.DatagramSocketClient#open open()} before
112      * invoking this method. This method will not close the OutputStream
113      * containing the file; you must close it after the method invocation.
114      * <p>
115      * @param filename The name of the file to receive.
116      * @param mode The TFTP mode of the transfer (one of the MODE constants).
117      * @param output The OutputStream to which the file should be written.
118      * @param host The remote host serving the file.
119      * @param port The port number of the remote TFTP server.
120      * @exception IOException If an I/O error occurs. The nature of the
121      * error will be reported in the message.
122      ***/

123     public int receiveFile(String JavaDoc filename, int mode, OutputStream JavaDoc output,
124                            InetAddress JavaDoc host, int port) throws IOException JavaDoc
125     {
126         int bytesRead, timeouts, lastBlock, block, hostPort, dataLength;
127         TFTPPacket sent, received = null;
128         TFTPErrorPacket error;
129         TFTPDataPacket data;
130         TFTPAckPacket ack = new TFTPAckPacket(host, port, 0);
131
132         beginBufferedOps();
133
134         dataLength = lastBlock = hostPort = bytesRead = 0;
135         block = 1;
136
137         if (mode == TFTP.ASCII_MODE)
138             output = new FromNetASCIIOutputStream(output);
139
140         sent =
141             new TFTPReadRequestPacket(host, port, filename, mode);
142
143 _sendPacket:
144         do
145         {
146             bufferedSend(sent);
147
148 _receivePacket:
149             while (true)
150             {
151                 timeouts = 0;
152                 while (timeouts < __maxTimeouts)
153                 {
154                     try
155                     {
156                         received = bufferedReceive();
157                         break;
158                     }
159                     catch (SocketException JavaDoc e)
160                     {
161                         if (++timeouts >= __maxTimeouts)
162                         {
163                             endBufferedOps();
164                             throw new IOException JavaDoc("Connection timed out.");
165                         }
166                         continue;
167                     }
168                     catch (InterruptedIOException JavaDoc e)
169                     {
170                         if (++timeouts >= __maxTimeouts)
171                         {
172                             endBufferedOps();
173                             throw new IOException JavaDoc("Connection timed out.");
174                         }
175                         continue;
176                     }
177                     catch (TFTPPacketException e)
178                     {
179                         endBufferedOps();
180                         throw new IOException JavaDoc("Bad packet: " + e.getMessage());
181                     }
182                 }
183
184                 // The first time we receive we get the port number and
185
// answering host address (for hosts with multiple IPs)
186
if (lastBlock == 0)
187                 {
188                     hostPort = received.getPort();
189                     ack.setPort(hostPort);
190                     if(!host.equals(received.getAddress()))
191                     {
192                         host = received.getAddress();
193                         ack.setAddress(host);
194                         sent.setAddress(host);
195                     }
196                 }
197
198                 // Comply with RFC 783 indication that an error acknowledgement
199
// should be sent to originator if unexpected TID or host.
200
if (host.equals(received.getAddress()) &&
201                         received.getPort() == hostPort)
202                 {
203
204                     switch (received.getType())
205                     {
206                     case TFTPPacket.ERROR:
207                         error = (TFTPErrorPacket)received;
208                         endBufferedOps();
209                         throw new IOException JavaDoc("Error code " + error.getError() +
210                                               " received: " + error.getMessage());
211                     case TFTPPacket.DATA:
212                         data = (TFTPDataPacket)received;
213                         dataLength = data.getDataLength();
214
215                         lastBlock = data.getBlockNumber();
216
217                         if (lastBlock == block)
218                         {
219                             try
220                             {
221                                 output.write(data.getData(), data.getDataOffset(),
222                                              dataLength);
223                             }
224                             catch (IOException JavaDoc e)
225                             {
226                                 error = new TFTPErrorPacket(host, hostPort,
227                                                             TFTPErrorPacket.OUT_OF_SPACE,
228                                                             "File write failed.");
229                                 bufferedSend(error);
230                                 endBufferedOps();
231                                 throw e;
232                             }
233                             ++block;
234                             break _receivePacket;
235                         }
236                         else
237                         {
238                             discardPackets();
239
240                             if (lastBlock == (block - 1))
241                                 continue _sendPacket; // Resend last acknowledgement.
242

243                             continue _receivePacket; // Start fetching packets again.
244
}
245                         //break;
246

247                     default:
248                         endBufferedOps();
249                         throw new IOException JavaDoc("Received unexpected packet type.");
250                     }
251                 }
252                 else
253                 {
254                     error = new TFTPErrorPacket(received.getAddress(),
255                                                 received.getPort(),
256                                                 TFTPErrorPacket.UNKNOWN_TID,
257                                                 "Unexpected host or port.");
258                     bufferedSend(error);
259                     continue _sendPacket;
260                 }
261
262                 // We should never get here, but this is a safety to avoid
263
// infinite loop. If only Java had the goto statement.
264
//break;
265
}
266
267             ack.setBlockNumber(lastBlock);
268             sent = ack;
269             bytesRead += dataLength;
270         } // First data packet less than 512 bytes signals end of stream.
271

272         while (dataLength == TFTPPacket.SEGMENT_SIZE);
273
274         bufferedSend(sent);
275         endBufferedOps();
276
277         return bytesRead;
278     }
279
280
281     /***
282      * Requests a named file from a remote host, writes the
283      * file to an OutputStream, closes the connection, and returns the number
284      * of bytes read. A local UDP socket must first be created by
285      * {@link org.apache.commons.net.DatagramSocketClient#open open()} before
286      * invoking this method. This method will not close the OutputStream
287      * containing the file; you must close it after the method invocation.
288      * <p>
289      * @param filename The name of the file to receive.
290      * @param mode The TFTP mode of the transfer (one of the MODE constants).
291      * @param output The OutputStream to which the file should be written.
292      * @param hostname The name of the remote host serving the file.
293      * @param port The port number of the remote TFTP server.
294      * @exception IOException If an I/O error occurs. The nature of the
295      * error will be reported in the message.
296      * @exception UnknownHostException If the hostname cannot be resolved.
297      ***/

298     public int receiveFile(String JavaDoc filename, int mode, OutputStream JavaDoc output,
299                            String JavaDoc hostname, int port)
300     throws UnknownHostException JavaDoc, IOException JavaDoc
301     {
302         return receiveFile(filename, mode, output, InetAddress.getByName(hostname),
303                            port);
304     }
305
306
307     /***
308      * Same as calling receiveFile(filename, mode, output, host, TFTP.DEFAULT_PORT).
309      *
310      * @param filename The name of the file to receive.
311      * @param mode The TFTP mode of the transfer (one of the MODE constants).
312      * @param output The OutputStream to which the file should be written.
313      * @param host The remote host serving the file.
314      * @exception IOException If an I/O error occurs. The nature of the
315      * error will be reported in the message.
316      ***/

317     public int receiveFile(String JavaDoc filename, int mode, OutputStream JavaDoc output,
318                            InetAddress JavaDoc host)
319     throws IOException JavaDoc
320     {
321         return receiveFile(filename, mode, output, host, DEFAULT_PORT);
322     }
323
324     /***
325      * Same as calling receiveFile(filename, mode, output, hostname, TFTP.DEFAULT_PORT).
326      *
327      * @param filename The name of the file to receive.
328      * @param mode The TFTP mode of the transfer (one of the MODE constants).
329      * @param output The OutputStream to which the file should be written.
330      * @param hostname The name of the remote host serving the file.
331      * @exception IOException If an I/O error occurs. The nature of the
332      * error will be reported in the message.
333      * @exception UnknownHostException If the hostname cannot be resolved.
334      ***/

335     public int receiveFile(String JavaDoc filename, int mode, OutputStream JavaDoc output,
336                            String JavaDoc hostname)
337     throws UnknownHostException JavaDoc, IOException JavaDoc
338     {
339         return receiveFile(filename, mode, output, InetAddress.getByName(hostname),
340                            DEFAULT_PORT);
341     }
342
343
344     /***
345      * Requests to send a file to a remote host, reads the file from an
346      * InputStream, sends the file to the remote host, and closes the
347      * connection. A local UDP socket must first be created by
348      * {@link org.apache.commons.net.DatagramSocketClient#open open()} before
349      * invoking this method. This method will not close the InputStream
350      * containing the file; you must close it after the method invocation.
351      * <p>
352      * @param filename The name the remote server should use when creating
353      * the file on its file system.
354      * @param mode The TFTP mode of the transfer (one of the MODE constants).
355      * @param host The remote host receiving the file.
356      * @param port The port number of the remote TFTP server.
357      * @exception IOException If an I/O error occurs. The nature of the
358      * error will be reported in the message.
359      ***/

360     public void sendFile(String JavaDoc filename, int mode, InputStream JavaDoc input,
361                          InetAddress JavaDoc host, int port) throws IOException JavaDoc
362     {
363         int bytesRead, timeouts, lastBlock, block, hostPort, dataLength, offset;
364         TFTPPacket sent, received = null;
365         TFTPErrorPacket error;
366         TFTPDataPacket data =
367             new TFTPDataPacket(host, port, 0, _sendBuffer, 4, 0);
368         ;
369         TFTPAckPacket ack;
370
371         beginBufferedOps();
372
373         dataLength = lastBlock = hostPort = bytesRead = 0;
374         block = 0;
375         boolean lastAckWait = false;
376
377         if (mode == TFTP.ASCII_MODE)
378             input = new ToNetASCIIInputStream(input);
379
380         sent =
381             new TFTPWriteRequestPacket(host, port, filename, mode);
382
383 _sendPacket:
384         do
385         {
386             bufferedSend(sent);
387
388 _receivePacket:
389             while (true)
390             {
391                 timeouts = 0;
392                 while (timeouts < __maxTimeouts)
393                 {
394                     try
395                     {
396                         received = bufferedReceive();
397                         break;
398                     }
399                     catch (SocketException JavaDoc e)
400                     {
401                         if (++timeouts >= __maxTimeouts)
402                         {
403                             endBufferedOps();
404                             throw new IOException JavaDoc("Connection timed out.");
405                         }
406                         continue;
407                     }
408                     catch (InterruptedIOException JavaDoc e)
409                     {
410                         if (++timeouts >= __maxTimeouts)
411                         {
412                             endBufferedOps();
413                             throw new IOException JavaDoc("Connection timed out.");
414                         }
415                         continue;
416                     }
417                     catch (TFTPPacketException e)
418                     {
419                         endBufferedOps();
420                         throw new IOException JavaDoc("Bad packet: " + e.getMessage());
421                     }
422                 }
423
424                 // The first time we receive we get the port number and
425
// answering host address (for hosts with multiple IPs)
426
if (lastBlock == 0)
427                 {
428                     hostPort = received.getPort();
429                     data.setPort(hostPort);
430                     if(!host.equals(received.getAddress()))
431                     {
432                         host = received.getAddress();
433                         data.setAddress(host);
434                         sent.setAddress(host);
435                     }
436                 }
437
438                 // Comply with RFC 783 indication that an error acknowledgement
439
// should be sent to originator if unexpected TID or host.
440
if (host.equals(received.getAddress()) &&
441                         received.getPort() == hostPort)
442                 {
443
444                     switch (received.getType())
445                     {
446                     case TFTPPacket.ERROR:
447                         error = (TFTPErrorPacket)received;
448                         endBufferedOps();
449                         throw new IOException JavaDoc("Error code " + error.getError() +
450                                               " received: " + error.getMessage());
451                     case TFTPPacket.ACKNOWLEDGEMENT:
452                         ack = (TFTPAckPacket)received;
453
454                         lastBlock = ack.getBlockNumber();
455
456                         if (lastBlock == block)
457                         {
458                             ++block;
459                             if (lastAckWait)
460                               break _sendPacket;
461                             else
462                               break _receivePacket;
463                         }
464                         else
465                         {
466                             discardPackets();
467
468                             if (lastBlock == (block - 1))
469                                 continue _sendPacket; // Resend last acknowledgement.
470

471                             continue _receivePacket; // Start fetching packets again.
472
}
473                         //break;
474

475                     default:
476                         endBufferedOps();
477                         throw new IOException JavaDoc("Received unexpected packet type.");
478                     }
479                 }
480                 else
481                 {
482                     error = new TFTPErrorPacket(received.getAddress(),
483                                                 received.getPort(),
484                                                 TFTPErrorPacket.UNKNOWN_TID,
485                                                 "Unexpected host or port.");
486                     bufferedSend(error);
487                     continue _sendPacket;
488                 }
489
490                 // We should never get here, but this is a safety to avoid
491
// infinite loop. If only Java had the goto statement.
492
//break;
493
}
494
495             dataLength = TFTPPacket.SEGMENT_SIZE;
496             offset = 4;
497             while (dataLength > 0 &&
498                     (bytesRead = input.read(_sendBuffer, offset, dataLength)) > 0)
499             {
500                 offset += bytesRead;
501                 dataLength -= bytesRead;
502             }
503
504             data.setBlockNumber(block);
505             data.setData(_sendBuffer, 4, offset - 4);
506             sent = data;
507         }
508         while (dataLength == 0 || lastAckWait);
509
510         endBufferedOps();
511     }
512
513
514     /***
515      * Requests to send a file to a remote host, reads the file from an
516      * InputStream, sends the file to the remote host, and closes the
517      * connection. A local UDP socket must first be created by
518      * {@link org.apache.commons.net.DatagramSocketClient#open open()} before
519      * invoking this method. This method will not close the InputStream
520      * containing the file; you must close it after the method invocation.
521      * <p>
522      * @param filename The name the remote server should use when creating
523      * the file on its file system.
524      * @param mode The TFTP mode of the transfer (one of the MODE constants).
525      * @param hostname The name of the remote host receiving the file.
526      * @param port The port number of the remote TFTP server.
527      * @exception IOException If an I/O error occurs. The nature of the
528      * error will be reported in the message.
529      * @exception UnknownHostException If the hostname cannot be resolved.
530      ***/

531     public void sendFile(String JavaDoc filename, int mode, InputStream JavaDoc input,
532                          String JavaDoc hostname, int port)
533     throws UnknownHostException JavaDoc, IOException JavaDoc
534     {
535         sendFile(filename, mode, input, InetAddress.getByName(hostname), port);
536     }
537
538
539     /***
540      * Same as calling sendFile(filename, mode, input, host, TFTP.DEFAULT_PORT).
541      *
542      * @param filename The name the remote server should use when creating
543      * the file on its file system.
544      * @param mode The TFTP mode of the transfer (one of the MODE constants).
545      * @param host The name of the remote host receiving the file.
546      * @exception IOException If an I/O error occurs. The nature of the
547      * error will be reported in the message.
548      * @exception UnknownHostException If the hostname cannot be resolved.
549      ***/

550     public void sendFile(String JavaDoc filename, int mode, InputStream JavaDoc input,
551                          InetAddress JavaDoc host)
552     throws IOException JavaDoc
553     {
554         sendFile(filename, mode, input, host, DEFAULT_PORT);
555     }
556
557     /***
558      * Same as calling sendFile(filename, mode, input, hostname, TFTP.DEFAULT_PORT).
559      *
560      * @param filename The name the remote server should use when creating
561      * the file on its file system.
562      * @param mode The TFTP mode of the transfer (one of the MODE constants).
563      * @param hostname The name of the remote host receiving the file.
564      * @exception IOException If an I/O error occurs. The nature of the
565      * error will be reported in the message.
566      * @exception UnknownHostException If the hostname cannot be resolved.
567      ***/

568     public void sendFile(String JavaDoc filename, int mode, InputStream JavaDoc input,
569                          String JavaDoc hostname)
570     throws UnknownHostException JavaDoc, IOException JavaDoc
571     {
572         sendFile(filename, mode, input, InetAddress.getByName(hostname),
573                  DEFAULT_PORT);
574     }
575 }
576
Popular Tags