View Javadoc

1   /*
2    * Created on Jun 11, 2003
3    *
4    */
5   package org.astrogrid.mySpace.mySpaceUtil;
6   
7   import java.io.File;
8   import org.globus.ftp.DataChannelAuthentication;
9   import org.globus.ftp.GridFTPClient;
10  import org.globus.ftp.Session;
11  import org.globus.io.streams.GlobusFileInputStream;
12  import org.globus.io.streams.GlobusFileOutputStream;
13  import org.globus.io.streams.GlobusInputStream;
14  import org.globus.io.streams.GlobusOutputStream;
15  import org.globus.io.streams.HTTPInputStream;
16  import org.globus.util.GlobusURL;
17  import org.gridforum.jgss.ExtendedGSSManager;
18  import org.ietf.jgss.GSSCredential;
19  
20  /***
21   * A manager for the download of a single data-set from a URL.
22   *
23   * The client should create a fresh FileTransfer for each transfer.
24   * There is no valid way to reuse one of these object for a second
25   * transfer.  The source and destination of the data are set when
26   * the FileTransfer is created.
27   *
28   * The data source may be either a single URL or an array of URLs
29   * that are mirrors of the same data-set.  In the latter case,
30   * the FileTransfer will start6 with the URL in element zero of
31   * the array and try URLs in turn until a successful transfer is
32   * achieved or until there are no more URLs.  The data destination
33   * is always a file on the local file-system.
34   *
35   * Exceptions while downloading are caught.  On catching an
36   * exception for a given URL, the class stores the exception
37   * and goes on to the next URL in the input set.  If there are
38   * no more URLs to try, then a FileTransferException is thrown.
39   * After the {@link transfer} completes or fails, the client may
40   * retrieve the exceptions stored for the individual download
41   * attempts by calling {@link getErrors}.  The URL, in the set of
42   * mirrors, that actually supplied the data may be retrieved by
43   * calling {@link getChosenUrl}.
44   *
45   * A FileTransfer is not MT-safe.
46   *
47   * @author Jia Yu
48   * @author Guy Rixon
49   */
50  public class FileTransfer {
51  
52    /***
53     * The mirrors of the data-source.
54     */
55    private String[]    urls;
56  
57    /***
58     * The destination for the transferred data.
59     */
60    private File        file;
61  
62    /***
63     * The mirror that actually gave a valid transfer.
64     */
65    private String      urlUsed;
66  
67    /***
68     * The error associated with each mirror.
69     * Errors are null where a mirror gave a successful
70     * transfer or where a mirror has not yet been used.
71     */
72    private Exception[] errors;
73  
74  
75    /***
76     * Creates a FileTransfer with a set of source URLs and
77     * one destination file.  The source URLs must be given
78     * in order of preference.
79     *
80     * @param sources     the list of mirror URLs from which to transfer.
81     * @param destination the name of the file in which to put the data.
82     */
83    public FileTransfer (final String[] sources, final String destination) {
84      this.urls   = sources;
85      this.file   = new File(destination);
86      this.errors = new Exception[sources.length];
87    }
88  
89  
90    /***
91     * Creates a FileTransfer with a single source URL and
92     * one destination file.
93     *
94     * @param source      the URL from which to transfer.
95     * @param destination the name of the file in which to put the data.
96     */
97    public FileTransfer (final String source, final String destination) {
98      String[] mirrors = {source};
99      this.urls        = mirrors;
100     this.file        = new File(destination);
101     this.errors      = new Exception[1];
102   }
103 
104 
105 
106   /***
107    * Executes the transfer using the sources and destination
108    * set during construction.  The given sources are tried in
109    * order until a transfer succeeds or there are no more sources.
110    *
111    * @throws FileTransferException if all sources fail.
112    */
113   public final void transfer () throws FileTransferException {
114     boolean success = false;
115     for (int i = 0; i < this.urls.length && !success; i++) {
116       try {
117         this.transfer(this.urls[i], this.file);
118         success = true;
119         this.urlUsed = this.urls[i];
120       }
121       catch (Exception e) {
122         this.errors[i] = e;
123       }
124     }
125     if (!success) {
126       throw new FileTransferException(this.file +
127                                       " was not obtained;" +
128                                       " all file-transfer protocols failed.");
129     }
130   }
131 
132 
133   /***
134    * Returns the URL used in the last transfer.  Returns null if
135    * no transfer has been made since construction.
136    *
137    * @return the URL used in the transfer.
138    */
139   public final String getChosenUrl () {
140     return this.urlUsed;
141   }
142 
143 
144   /***
145    * Returns the Exceptions associated with each source URL.
146    * Before the transfer all the exceptions are null.  After
147    * the transfer, exceptions are non-null only where a source
148    * URL has been tried and has failed.  I.e., the exceptions
149    * are non-null at the start of the returned array and are
150    * null for the successful URL and any URL following it.
151    *
152    * @return array of Exceptions mapped on-for-one to the
153    * array of source URLs given at construction.
154    */
155   public final Exception[] getErrors () {
156     return this.errors;
157   }
158 
159 
160   /***
161    * Attempts one transfer, using one source URL.
162    *
163    * @param urlStr the URL for the data source.
164    * @param localFile the destination of the data.
165    *
166    * @throws FileTransferException if the transfer does not complete.
167    */
168   private void transfer(final String urlStr, final File localFile)
169       throws FileTransferException {
170 
171     // Catch any exception and rethrow as a FileTransferException
172     // with the original exception as the cause.
173     try {
174 
175       // Parse the source URL.  GlobusURL is used insted of java.net.URL
176       // in order to include gsiftp URLs.
177       GlobusURL url = new GlobusURL(urlStr);
178 
179       // Do the transfer.
180       if (url.getProtocol().equalsIgnoreCase("gsiftp") ||
181           url.getProtocol().equalsIgnoreCase("gridftp")) {
182         this.useGridFtp(url, localFile);
183       }
184       else if (url.getProtocol().equalsIgnoreCase("http")) {
185         this.useHttp(url, localFile);
186       }
187       else if (url.getProtocol().equalsIgnoreCase("file")) {
188         this.useFile(url, localFile);
189       }
190       else {
191         System.out.println("FileTransfer.transfer: unknown protocol: "
192                            + url.getProtocol());
193         throw new Exception("Unknown protocol: " + url.getProtocol());
194       }
195 
196     }
197     catch (Exception e) {
198       String message = "File transfer from "
199                        + urlStr
200                        + " to "
201                        + localFile.getPath()
202                        + " failed.";
203       System.out.println(message + " " + e.getMessage());
204       throw new FileTransferException(message, e);
205     }
206   }
207 
208 
209   /***
210    * Transfers from a GridFTP URL (scheme gsiftp or gridftp).
211    *
212    * @param url       the data source.
213    * @param localFile the data destination.
214    *
215    * @throws Exception if the transfer fails.
216    */
217   private void useGridFtp (final GlobusURL url, final File localFile)
218       throws Exception {
219 
220     GSSCredential      cred      = null;
221     GridFTPClient      hotClient = null;
222 
223     try {
224       hotClient = new GridFTPClient(url.getHost(), url.getPort());
225       System.out.println("FileTransfer.transfer : created a GridFTP client.");
226 
227       // Create a credential
228       System.out.println("FileTransfer.transfer : creaing credentials");
229       ExtendedGSSManager manager =
230           (ExtendedGSSManager) ExtendedGSSManager.getInstance();
231       cred = manager.createCredential(GSSCredential.INITIATE_AND_ACCEPT);
232 
233       // Authenticate to the server
234       System.out.println("FileTransfer.transfer : authenticating");
235       hotClient.authenticate(cred);
236 
237       // Set security parameters such as data channel authentication
238       // (defined by the GridFTP protocol) and data channel
239       // protection (defined by RFC 2228).
240       // If these are not specified, data channels are authenticated
241       // by default.
242       System.out.println("FileTransfer.transfer : setting data channel");
243       hotClient.setDataChannelAuthentication(DataChannelAuthentication.NONE);
244       hotClient.setType(Session.TYPE_IMAGE);
245 
246       // Download the data.
247       System.out.println("FileTransfer.transfer : getting data");
248       if (!hotClient.exists(url.getPath())) {
249         throw new FileTransferException("the File does not exist");
250       }
251       else {
252        hotClient.get(url.getPath(), localFile);
253       }
254 
255     }
256     catch (Exception e) {
257       throw e;
258     }
259     finally {
260       if (hotClient != null) hotClient.close();
261     }
262   }
263 
264 
265   /***
266    * Attempts a transfer from an HTTP URL.
267    *
268    * @param url       the data source.
269    * @param localFile the data destination.
270    *
271    * @throws Exception if the transfer fails
272    */
273   private void useHttp (final GlobusURL url, final File localFile)
274       throws Exception {
275 
276     HTTPInputStream        in  = null;
277     GlobusFileOutputStream out = null;
278 
279     try {
280       in  = new HTTPInputStream(url.getHost(), url.getPort(), url.getPath());
281       out = new GlobusFileOutputStream(localFile.getPath(), false);
282       byte[] buffer = new byte[2048];
283       int    bytes;
284       while (true) {
285         bytes = in.read(buffer);
286         if (bytes == -1) break;
287         out.write(buffer, 0, bytes);
288         out.flush();
289       }
290     }
291     catch (Exception e) {
292       throw e;
293     }
294     finally {
295       if (in  != null) in.close();
296       if (out != null) out.close();
297     }
298   }
299 
300 
301   /***
302    * Attempts a transfer from a file URL.
303    *
304    * @param url       the data source.
305    * @param localFile the data destination.
306    *
307    * @throws Exception if the transfer fails.
308    */
309   private void useFile (final GlobusURL url, final File localFile)
310       throws Exception {
311 
312     GlobusFileInputStream  in  = null;
313     GlobusFileOutputStream out = null;
314 
315     System.out.println("FileTransfer.transfer : using 'file' protocol.");
316 
317     try {
318       in  = new GlobusFileInputStream(url.getPath());
319       out = new GlobusFileOutputStream(localFile.getPath(), false);
320       byte[] buffer = new byte[2048];
321       int    bytes;
322       while (true) {
323         bytes = in.read(buffer);
324         if (bytes == -1) break;
325         out.write(buffer, 0, bytes);
326         out.flush();
327       }
328     }
329     catch (Exception e) {
330       throw e;
331     }
332     finally {
333       if (in  != null) in.close();
334       if (out != null) out.close();
335     }
336   }
337 
338 }