KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > vfs > provider > ftp > FtpFileObject


1 /*
2  * Copyright 2002-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.vfs.provider.ftp;
17
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20 import org.apache.commons.net.ftp.FTPFile;
21 import org.apache.commons.vfs.FileName;
22 import org.apache.commons.vfs.FileObject;
23 import org.apache.commons.vfs.FileSystemException;
24 import org.apache.commons.vfs.FileType;
25 import org.apache.commons.vfs.RandomAccessContent;
26 import org.apache.commons.vfs.provider.AbstractFileObject;
27 import org.apache.commons.vfs.provider.UriParser;
28 import org.apache.commons.vfs.util.Messages;
29 import org.apache.commons.vfs.util.MonitorInputStream;
30 import org.apache.commons.vfs.util.MonitorOutputStream;
31 import org.apache.commons.vfs.util.RandomAccessMode;
32
33 import java.io.IOException JavaDoc;
34 import java.io.InputStream JavaDoc;
35 import java.io.OutputStream JavaDoc;
36 import java.util.Calendar JavaDoc;
37 import java.util.Collections JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.Map JavaDoc;
40 import java.util.TreeMap JavaDoc;
41
42 /**
43  * An FTP file.
44  *
45  * @author <a HREF="mailto:adammurdoch@apache.org">Adam Murdoch</a>
46  */

47 public class FtpFileObject
48     extends AbstractFileObject
49 {
50     private Log log = LogFactory.getLog(FtpFileObject.class);
51
52     private static final Map JavaDoc EMPTY_FTP_FILE_MAP = Collections.unmodifiableMap(new TreeMap JavaDoc());
53
54     private final FtpFileSystem ftpFs;
55     private final String JavaDoc relPath;
56
57     // Cached info
58
private FTPFile fileInfo;
59     private Map JavaDoc children;
60     private FileObject linkDestination;
61
62     protected FtpFileObject(final FileName name,
63                             final FtpFileSystem fileSystem,
64                             final FileName rootName)
65         throws FileSystemException
66     {
67         super(name, fileSystem);
68         ftpFs = fileSystem;
69         String JavaDoc relPath = UriParser.decode(rootName.getRelativeName(name));
70         if (".".equals(relPath))
71         {
72             // do not use the "." as path against the ftp-server
73
// e.g. the uu.net ftp-server do a recursive listing then
74
// this.relPath = UriParser.decode(rootName.getPath());
75
// this.relPath = ".";
76
this.relPath = null;
77         }
78         else
79         {
80             this.relPath = relPath;
81         }
82     }
83
84     /**
85      * Called by child file objects, to locate their ftp file info.
86      *
87      * @param name the filename in its native form ie. without uri stuff (%nn)
88      * @param flush recreate children cache
89      */

90     private FTPFile getChildFile(final String JavaDoc name, final boolean flush) throws IOException JavaDoc
91     {
92         if (flush)
93         {
94             children = null;
95         }
96
97         // List the children of this file
98
doGetChildren();
99
100         // Look for the requested child
101
FTPFile ftpFile = (FTPFile) children.get(name);
102         return ftpFile;
103     }
104
105     /**
106      * Fetches the children of this file, if not already cached.
107      */

108     private void doGetChildren() throws IOException JavaDoc
109     {
110         if (children != null)
111         {
112             return;
113         }
114
115         final FtpClient client = ftpFs.getClient();
116         try
117         {
118             String JavaDoc key = FtpFileSystemConfigBuilder.getInstance().getEntryParser(getFileSystem().getFileSystemOptions());
119             final FTPFile[] tmpChildren = client.listFiles(key, relPath);
120             if (tmpChildren == null || tmpChildren.length == 0)
121             {
122                 children = EMPTY_FTP_FILE_MAP;
123             }
124             else
125             {
126                 children = new TreeMap JavaDoc();
127
128                 // Remove '.' and '..' elements
129
for (int i = 0; i < tmpChildren.length; i++)
130                 {
131                     final FTPFile child = tmpChildren[i];
132                     if (child == null)
133                     {
134                         if (log.isDebugEnabled())
135                         {
136                             log.debug(Messages.getString("vfs.provider.ftp/invalid-directory-entry.debug",
137                                 new Object JavaDoc[]
138                                     {
139                                         new Integer JavaDoc(i), relPath
140                                     }));
141                         }
142                         continue;
143                     }
144                     if (!".".equals(child.getName())
145                         && !"..".equals(child.getName()))
146                     {
147                         children.put(child.getName(), child);
148                     }
149                 }
150             }
151         }
152         finally
153         {
154             ftpFs.putClient(client);
155         }
156     }
157
158     /**
159      * Attaches this file object to its file resource.
160      */

161     protected void doAttach()
162         throws IOException JavaDoc
163     {
164         // Get the parent folder to find the info for this file
165
getInfo(false);
166     }
167
168     /**
169      * Fetches the info for this file.
170      */

171     private void getInfo(boolean flush) throws IOException JavaDoc
172     {
173         final FtpFileObject parent = (FtpFileObject) getParent();
174         FTPFile newFileInfo;
175         if (parent != null)
176         {
177             newFileInfo = parent.getChildFile(UriParser.decode(getName().getBaseName()), flush);
178         }
179         else
180         {
181             // Assume the root is a directory and exists
182
newFileInfo = new FTPFile();
183             newFileInfo.setType(FTPFile.DIRECTORY_TYPE);
184         }
185
186         this.fileInfo = newFileInfo;
187     }
188
189     /**
190      * Detaches this file object from its file resource.
191      */

192     protected void doDetach()
193     {
194         this.fileInfo = null;
195         children = null;
196     }
197
198     /**
199      * Called when the children of this file change.
200      */

201     protected void onChildrenChanged(FileName child, FileType newType)
202     {
203         if (children != null && newType.equals(FileType.IMAGINARY))
204         {
205             try
206             {
207                 children.remove(UriParser.decode(child.getBaseName()));
208             }
209             catch (FileSystemException e)
210             {
211                 throw new RuntimeException JavaDoc(e.getMessage());
212             }
213         }
214         else
215         {
216             // if child was added we have to rescan the children
217
// TODO - get rid of this
218
children = null;
219         }
220     }
221
222     /**
223      * Called when the type or content of this file changes.
224      */

225     protected void onChange() throws IOException JavaDoc
226     {
227         children = null;
228
229         if (getType().equals(FileType.IMAGINARY))
230         {
231             // file is deleted, avoid server lookup
232
this.fileInfo = null;
233             return;
234         }
235
236         getInfo(true);
237     }
238
239     /**
240      * Determines the type of the file, returns null if the file does not
241      * exist.
242      */

243     protected FileType doGetType()
244         throws Exception JavaDoc
245     {
246         if (this.fileInfo == null)
247         {
248             return FileType.IMAGINARY;
249         }
250         else if (this.fileInfo.isDirectory())
251         {
252             return FileType.FOLDER;
253         }
254         else if (this.fileInfo.isFile())
255         {
256             return FileType.FILE;
257         }
258         else if (this.fileInfo.isSymbolicLink())
259         {
260             return getLinkDestination().getType();
261         }
262
263         throw new FileSystemException("vfs.provider.ftp/get-type.error", getName());
264     }
265
266     private FileObject getLinkDestination() throws FileSystemException
267     {
268         if (linkDestination == null)
269         {
270             final String JavaDoc path = this.fileInfo.getLink();
271             FileName relativeTo = getName().getParent();
272             if (relativeTo == null)
273             {
274                 relativeTo = getName();
275             }
276             FileName linkDestinationName = getFileSystem().getFileSystemManager().resolveName(relativeTo, path);
277             linkDestination = getFileSystem().resolveFile(linkDestinationName);
278         }
279
280         return linkDestination;
281     }
282
283     protected FileObject[] doListChildrenResolved() throws Exception JavaDoc
284     {
285         if (this.fileInfo.isSymbolicLink())
286         {
287             return getLinkDestination().getChildren();
288         }
289
290         return null;
291     }
292
293     /**
294      * Lists the children of the file.
295      */

296     protected String JavaDoc[] doListChildren()
297         throws Exception JavaDoc
298     {
299         // List the children of this file
300
doGetChildren();
301
302         // TODO - get rid of this children stuff
303
final String JavaDoc[] childNames = new String JavaDoc[children.size()];
304         int childNum = -1;
305         Iterator JavaDoc iterChildren = children.values().iterator();
306         while (iterChildren.hasNext())
307         {
308             childNum++;
309             final FTPFile child = (FTPFile) iterChildren.next();
310             childNames[childNum] = child.getName();
311         }
312
313         return UriParser.encode(childNames);
314     }
315
316     /**
317      * Deletes the file.
318      */

319     protected void doDelete() throws Exception JavaDoc
320     {
321         final boolean ok;
322         final FtpClient ftpClient = ftpFs.getClient();
323         try
324         {
325             if (this.fileInfo.isDirectory())
326             {
327                 ok = ftpClient.removeDirectory(relPath);
328             }
329             else
330             {
331                 ok = ftpClient.deleteFile(relPath);
332             }
333         }
334         finally
335         {
336             ftpFs.putClient(ftpClient);
337         }
338
339         if (!ok)
340         {
341             throw new FileSystemException("vfs.provider.ftp/delete-file.error", getName());
342         }
343         this.fileInfo = null;
344         children = EMPTY_FTP_FILE_MAP;
345     }
346
347     /**
348      * Renames the file
349      */

350     protected void doRename(FileObject newfile) throws Exception JavaDoc
351     {
352         final boolean ok;
353         final FtpClient ftpClient = ftpFs.getClient();
354         try
355         {
356             String JavaDoc oldName = getName().getPath();
357             String JavaDoc newName = newfile.getName().getPath();
358             ok = ftpClient.rename(oldName, newName);
359         }
360         finally
361         {
362             ftpFs.putClient(ftpClient);
363         }
364
365         if (!ok)
366         {
367             throw new FileSystemException("vfs.provider.ftp/rename-file.error", new Object JavaDoc[]{getName().toString(), newfile});
368         }
369         this.fileInfo = null;
370         children = EMPTY_FTP_FILE_MAP;
371     }
372
373     /**
374      * Creates this file as a folder.
375      */

376     protected void doCreateFolder()
377         throws Exception JavaDoc
378     {
379         final boolean ok;
380         final FtpClient client = ftpFs.getClient();
381         try
382         {
383             ok = client.makeDirectory(relPath);
384         }
385         finally
386         {
387             ftpFs.putClient(client);
388         }
389
390         if (!ok)
391         {
392             throw new FileSystemException("vfs.provider.ftp/create-folder.error", getName());
393         }
394     }
395
396     /**
397      * Returns the size of the file content (in bytes).
398      */

399     protected long doGetContentSize() throws Exception JavaDoc
400     {
401         if (this.fileInfo.isSymbolicLink())
402         {
403             return getLinkDestination().getContent().getSize();
404         }
405         else
406         {
407             return this.fileInfo.getSize();
408         }
409     }
410
411     /**
412      * get the last modified time on an ftp file
413      *
414      * @see org.apache.commons.vfs.provider.AbstractFileObject#doGetLastModifiedTime()
415      */

416     protected long doGetLastModifiedTime() throws Exception JavaDoc
417     {
418         if (this.fileInfo.isSymbolicLink())
419         {
420             return getLinkDestination().getContent().getLastModifiedTime();
421         }
422         else
423         {
424             Calendar JavaDoc timestamp = this.fileInfo.getTimestamp();
425             if (timestamp == null)
426             {
427                 return 0L;
428             }
429             else
430             {
431                 return (timestamp.getTime().getTime());
432             }
433         }
434     }
435
436     /**
437      * Creates an input stream to read the file content from.
438      */

439     protected InputStream doGetInputStream() throws Exception JavaDoc
440     {
441         final FtpClient client = ftpFs.getClient();
442         final InputStream instr = client.retrieveFileStream(relPath);
443         return new FtpInputStream(client, instr);
444     }
445
446     protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception JavaDoc
447     {
448         return new FtpRandomAccessContent(this, mode);
449     }
450
451     /**
452      * Creates an output stream to write the file content to.
453      */

454     protected OutputStream doGetOutputStream(boolean bAppend)
455         throws Exception JavaDoc
456     {
457         final FtpClient client = ftpFs.getClient();
458         OutputStream out = null;
459         if (bAppend)
460         {
461             out = client.appendFileStream(relPath);
462         }
463         else
464         {
465             out = client.storeFileStream(relPath);
466         }
467
468         if (out == null)
469         {
470             throw new FileSystemException("vfs.provider.ftp/output-error.debug", new Object JavaDoc[]
471                 {
472                     this.getName(),
473                     client.getReplyString()
474                 });
475         }
476
477         return new FtpOutputStream(client, out);
478     }
479
480     String JavaDoc getRelPath()
481     {
482         return relPath;
483     }
484
485     FtpInputStream getInputStream(long filePointer) throws IOException JavaDoc
486     {
487         final FtpClient client = ftpFs.getClient();
488         final InputStream instr = client.retrieveFileStream(relPath, filePointer);
489         if (instr == null)
490         {
491             throw new FileSystemException("vfs.provider.ftp/input-error.debug", new Object JavaDoc[]
492                 {
493                     this.getName(),
494                     client.getReplyString()
495                 });
496         }
497         return new FtpInputStream(client, instr);
498     }
499
500     /**
501      * An InputStream that monitors for end-of-file.
502      */

503     class FtpInputStream
504         extends MonitorInputStream
505     {
506         private final FtpClient client;
507
508         public FtpInputStream(final FtpClient client, final InputStream in)
509         {
510             super(in);
511             this.client = client;
512         }
513
514         void abort() throws IOException JavaDoc
515         {
516             client.abort();
517             close();
518         }
519
520         /**
521          * Called after the stream has been closed.
522          */

523         protected void onClose() throws IOException JavaDoc
524         {
525             final boolean ok;
526             try
527             {
528                 ok = client.completePendingCommand();
529             }
530             finally
531             {
532                 ftpFs.putClient(client);
533             }
534
535             if (!ok)
536             {
537                 throw new FileSystemException("vfs.provider.ftp/finish-get.error", getName());
538             }
539         }
540     }
541
542     /**
543      * An OutputStream that monitors for end-of-file.
544      */

545     private class FtpOutputStream
546         extends MonitorOutputStream
547     {
548         private final FtpClient client;
549
550         public FtpOutputStream(final FtpClient client, final OutputStream outstr)
551         {
552             super(outstr);
553             this.client = client;
554         }
555
556         /**
557          * Called after this stream is closed.
558          */

559         protected void onClose() throws IOException JavaDoc
560         {
561             final boolean ok;
562             try
563             {
564                 ok = client.completePendingCommand();
565             }
566             finally
567             {
568                 ftpFs.putClient(client);
569             }
570
571             if (!ok)
572             {
573                 throw new FileSystemException("vfs.provider.ftp/finish-put.error", getName());
574             }
575         }
576     }
577 }
578
Popular Tags