KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > drftpd > remotefile > LinkedRemoteFile


1 /*
2  * This file is part of DrFTPD, Distributed FTP Daemon.
3  *
4  * DrFTPD is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * DrFTPD is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with DrFTPD; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */

18 package net.sf.drftpd.remotefile;
19
20 import java.io.FileNotFoundException JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.io.Serializable JavaDoc;
23 import java.rmi.RemoteException JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Collection JavaDoc;
26 import java.util.Collections JavaDoc;
27 import java.util.Hashtable JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.Set JavaDoc;
32 import java.util.Stack JavaDoc;
33 import java.util.StringTokenizer JavaDoc;
34
35 import net.sf.drftpd.FatalException;
36 import net.sf.drftpd.FileExistsException;
37 import net.sf.drftpd.NoAvailableSlaveException;
38 import net.sf.drftpd.ObjectNotFoundException;
39 import net.sf.drftpd.PermissionDeniedException;
40 import net.sf.drftpd.SFVFile;
41 import net.sf.drftpd.SlaveUnavailableException;
42 import net.sf.drftpd.master.RemoteSlave;
43 import net.sf.drftpd.master.config.FtpConfig;
44 import net.sf.drftpd.slave.Slave;
45 import net.sf.drftpd.slave.Transfer;
46 import net.sf.drftpd.slave.TransferStatus;
47 import net.sf.drftpd.util.ListUtils;
48
49 import org.apache.log4j.Level;
50 import org.apache.log4j.Logger;
51
52 /**
53  * Represents the file attributes of a remote file.
54  *
55  * @author mog
56  * @version $Id: LinkedRemoteFile.java,v 1.145.2.3 2004/06/25 20:31:36 mog Exp $
57  */

58 public class LinkedRemoteFile
59     implements Serializable JavaDoc, Comparable JavaDoc, LinkedRemoteFileInterface {
60     public static class CaseInsensitiveHashtable extends Hashtable JavaDoc {
61
62         public CaseInsensitiveHashtable() {
63             super();
64         }
65
66         public CaseInsensitiveHashtable(int initialCapacity) {
67             super(initialCapacity);
68         }
69
70         public CaseInsensitiveHashtable(
71             int initialCapacity,
72             float loadFactor) {
73             super(initialCapacity, loadFactor);
74         }
75
76         public CaseInsensitiveHashtable(Map JavaDoc t) {
77             super(t);
78         }
79
80     }
81     public static class NonExistingFile {
82         private LinkedRemoteFile _file;
83         private String JavaDoc _path;
84         public NonExistingFile(LinkedRemoteFile file, String JavaDoc path) {
85             _file = file;
86             _path = path;
87         }
88
89         /**
90          * Return true if getPath() returns a null value, i.e. <!-- --> returns
91          * true if the file exists.
92          */

93         public boolean exists() {
94             return _path == null;
95         }
96
97         public LinkedRemoteFile getFile() {
98             return _file;
99         }
100
101         public String JavaDoc getPath() {
102             return _path;
103         }
104
105         /**
106          * Returns true if getPath() returns a non-null value, i.e. <!-- -->
107          * returns false if the file exists.
108          */

109         public boolean hasPath() {
110             return _path != null;
111         }
112
113         public String JavaDoc toString() {
114             return "[NonExistingFile:file="
115                 + getFile().getPath()
116                 + ",path="
117                 + getPath()
118                 + "]";
119         }
120     }
121     private static final Logger logger =
122         Logger.getLogger(LinkedRemoteFile.class.getName());
123     static final long serialVersionUID = 3585958839961835107L;
124
125     public static void recursiveRenameLoop(
126         LinkedRemoteFile fromDir,
127         LinkedRemoteFile toDir) {
128         logger.debug(
129             "recursiveRenameLoop("
130                 + fromDir.getPath()
131                 + ", "
132                 + toDir.getPath()
133                 + ")");
134         for (Iterator JavaDoc iter =
135             new ArrayList JavaDoc(fromDir.getMap().values()).iterator();
136             iter.hasNext();
137             ) {
138             LinkedRemoteFile fromFile = (LinkedRemoteFile) iter.next();
139
140             LinkedRemoteFile toFile;
141             try {
142                 toFile = (LinkedRemoteFile) toDir.getFile(fromFile.getName());
143             } catch (QueuedDeletionException e) {
144                 fromFile.delete();
145                 continue;
146             } catch (FileNotFoundException JavaDoc e) {
147                 toFile = toDir.putFile(fromFile);
148             }
149             if (fromFile.isDirectory()) {
150                 recursiveRenameLoop(fromFile, toFile);
151             } else {
152                 recursiveRenameLoopFile(fromFile, toFile);
153             }
154         }
155         if (fromDir.isEmpty()) {
156             fromDir.delete();
157         }
158     }
159
160     public static void recursiveRenameLoopFile(
161         LinkedRemoteFile fromFile,
162         LinkedRemoteFile toFile) {
163         logger.debug(
164             "recursiveRenameLoopFile("
165                 + fromFile.getPath()
166                 + ", "
167                 + toFile.getPath());
168
169         Iterator JavaDoc iterator = new ArrayList JavaDoc(fromFile.getSlaves()).iterator();
170         while (iterator.hasNext()) {
171             RemoteSlave rslave = (RemoteSlave) iterator.next();
172             if (rslave.isAvailable()) {
173                 toFile.addSlave(rslave);
174                 fromFile.removeSlave(rslave);
175             } else {
176                 logger.debug(rslave + " is offline");
177                 fromFile.queueRename(toFile);
178             }
179         }
180     }
181
182     private static void recursiveSetRSlaveAndConfig(
183         LinkedRemoteFile dir,
184         FtpConfig ftpConfig,
185         RemoteSlave rslave) {
186
187         dir._ftpConfig = ftpConfig;
188         if (dir.isFile()) {
189             dir.addSlave(rslave);
190         }
191         if (dir.isDirectory()) {
192             for (Iterator JavaDoc iter = dir.getFiles().iterator(); iter.hasNext();) {
193                 recursiveSetRSlaveAndConfig(
194                     (LinkedRemoteFile) iter.next(),
195                     ftpConfig,
196                     rslave);
197             }
198         }
199     }
200     private long _checkSum;
201
202     private CaseInsensitiveHashtable _files;
203     private transient FtpConfig _ftpConfig;
204     private String JavaDoc _group;
205     //private Random rand = new Random();
206

207     //private String path;
208
private boolean _isDeleted = false;
209     private long _lastModified;
210
211     private long _length;
212     private String JavaDoc _link;
213     private String JavaDoc _name;
214     private String JavaDoc _owner;
215
216     private LinkedRemoteFile _parent;
217     /////////////////////// SLAVES
218
protected List JavaDoc _slaves;
219     private long _xfertime = 0;
220
221     protected SFVFile sfvFile;
222     /**
223      * Creates an empty RemoteFile directory, usually used as an empty root
224      * directory that <link>{merge()} </link> can be called on.
225      *
226      * Used if no file database exists to start a tree from scratch.
227      */

228     public LinkedRemoteFile(FtpConfig ftpConfig) {
229         _ftpConfig = ftpConfig;
230
231         _lastModified = System.currentTimeMillis();
232         _length = 0;
233         _parent = null;
234         _name = "";
235         _files = new CaseInsensitiveHashtable();
236         _slaves = Collections.synchronizedList(new ArrayList JavaDoc(1));
237     }
238
239     /**
240      * Creates a RemoteFile from file or creates a directory tree
241      * representation.
242      *
243      * Used by DirectoryRemoteFile. Called by other constructor,
244      * ConnectionManager is null if called from SlaveImpl.
245      *
246      * They all end up here.
247      *
248      * @param parent
249      * the parent of this file
250      * @param file
251      * file that this RemoteFile object should represent.
252      */

253     private LinkedRemoteFile(
254         LinkedRemoteFile parent,
255         RemoteFileInterface file,
256         FtpConfig cfg) {
257         this(parent, file, file.getName(), cfg);
258     }
259
260     private LinkedRemoteFile(
261         LinkedRemoteFile parent,
262         RemoteFileInterface file,
263         String JavaDoc name,
264         FtpConfig cfg) {
265
266         if (!ListUtils.isLegalFileName(name))
267             throw new IllegalArgumentException JavaDoc("Illegal filename");
268
269         if (!file.isFile() && !file.isDirectory())
270             throw new IllegalArgumentException JavaDoc(
271                 "File is not a file nor a directory: " + file);
272
273         if (_length == -1)
274             throw new IllegalArgumentException JavaDoc("length() == -1 for " + file);
275
276         _ftpConfig = cfg;
277         _lastModified = file.lastModified();
278         _isDeleted = file.isDeleted();
279         setOwner(file.getUsername());
280         setGroup(file.getGroupname());
281         _checkSum = file.getCheckSumCached();
282         _parent = parent;
283         if (file.isLink()) {
284             _link = file.getLinkPath();
285         }
286         if (parent == null) {
287             _name = "";
288         } else {
289             _name = name.toString();
290         }
291
292         if (file.isFile()) {
293             _length = file.length();
294             _slaves =
295                 Collections.synchronizedList(new ArrayList JavaDoc(file.getSlaves()));
296             try {
297                 getParentFile().addSize(length());
298             } catch (FileNotFoundException JavaDoc ok) {
299                 //thrown if this is the root dir
300
}
301         } else if (file.isDirectory()) {
302             //RemoteFileInterface dir[] = file.listFiles();
303
// if (name != "" && dir.length == 0)
304
// throw new FatalException(
305
// "Constructor called with empty dir: " + file);
306
_files =
307                     new CaseInsensitiveHashtable(file.getFiles().size());
308             Stack JavaDoc dirstack = new Stack JavaDoc();
309             //for (int i = 0; i < dir.length; i++) {
310
for (Iterator JavaDoc iter = file.getFiles().iterator(); iter.hasNext();) {
311                 RemoteFileInterface file2 = (RemoteFileInterface) iter.next();
312                 //RemoteFileInterface file2 = dir[i];
313
if (file2.isDirectory()) {
314                     dirstack.push(file2);
315                     continue;
316                 }
317                 //the constructor takes care of addSize()
318
_files.put(
319                     file2.getName(),
320                     new LinkedRemoteFile(this, file2, _ftpConfig));
321             }
322
323             Iterator JavaDoc i = dirstack.iterator();
324             while (i.hasNext()) {
325                 RemoteFileInterface file2 = (RemoteFileInterface) i.next();
326                 String JavaDoc filename = file2.getName();
327                 //the constructor takes care of addSize()
328
_files.put(
329                     filename,
330                     new LinkedRemoteFile(this, file2, _ftpConfig));
331             }
332         } else {
333             throw new RuntimeException JavaDoc();
334         }
335         //parent == null if creating root dir
336
}
337
338     /**
339      * Creates a root directory (parent == null) that FileRemoteFile or
340      * JDOMRemoteFile is merged on.
341      *
342      * Also called with null ConnectionManager from slave
343      */

344     public LinkedRemoteFile(RemoteFileInterface file, FtpConfig cfg)
345         throws IOException JavaDoc {
346         this(null, file, cfg);
347     }
348
349     /**
350      * Updates lastMofidied() on this directory, use putFile() to avoid it.
351      */

352     public LinkedRemoteFile addFile(AbstractRemoteFile file) {
353         _lastModified = System.currentTimeMillis();
354         return putFile(file);
355     }
356
357     protected synchronized void addSize(long size) {
358         _length += size;
359         // logger.debug(
360
// this +" got " + size + " added, now " + _length,
361
// new Throwable());
362
try {
363             getParentFile().addSize(size);
364         } catch (FileNotFoundException JavaDoc done) {
365         }
366     }
367
368     public void addSlave(RemoteSlave slave) {
369         if (_slaves == null) //isDirectory()
370
throw new IllegalStateException JavaDoc("Cannot addSlave() on a directory");
371         if (slave == null)
372             throw new NullPointerException JavaDoc();
373
374         // we get lots of duplicate adds when merging and the slave is already
375
// in the file database
376
if (_slaves.contains(slave)) {
377             return;
378         }
379         _slaves.add(slave);
380     }
381
382     /**
383      * @throws ClassCastException
384      * if object is not an instance of RemoteFileInterface.
385      * @see java.lang.Comparable#compareTo(java.lang.Object)
386      */

387     public int compareTo(Object JavaDoc o) {
388         return getName().compareTo(((RemoteFileInterface) o).getName());
389     }
390
391     public LinkedRemoteFile createDirectories(String JavaDoc path) {
392         NonExistingFile nef = lookupNonExistingFile(path);
393         if (!nef.hasPath())
394             throw new RuntimeException JavaDoc("createDirectories called on already existing directory");
395         LinkedRemoteFile dir = nef.getFile();
396         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(nef.getPath(), "/");
397         while (st.hasMoreTokens()) {
398             try {
399                 dir.createDirectory(st.nextToken());
400             } catch (FileExistsException e) {
401                 throw new RuntimeException JavaDoc(e);
402             }
403         }
404         return dir;
405     }
406
407     public LinkedRemoteFile createDirectory(String JavaDoc fileName)
408         throws FileExistsException {
409         return createDirectory(null, null, fileName);
410     }
411
412     public LinkedRemoteFile createDirectory(
413         String JavaDoc owner,
414         String JavaDoc group,
415         String JavaDoc fileName)
416         throws FileExistsException {
417         // LinkedRemoteFile existingfile = (LinkedRemoteFile)
418
// _files.get(fileName);
419
// //throws NullPointerException on non-existing directories
420
// if (existingfile.isDeleted())
421
// existingfile.delete();
422
// existingfile = (LinkedRemoteFile) _files.get(fileName);
423
if (hasFile(fileName)) {
424             throw new FileExistsException(
425                 fileName + " already exists in this directory");
426         }
427
428         LinkedRemoteFile file =
429             addFile(
430                 new StaticRemoteFile(
431                     null,
432                     fileName,
433                     owner,
434                     group,
435                     0L,
436                     System.currentTimeMillis()));
437         logger.info("Created directory " + file);
438         _lastModified = System.currentTimeMillis();
439         return file;
440     }
441
442     /**
443      * Deletes a file or directory, if slaves are offline, the file cannot be
444      * deleted. To work around this, the file gets a deleted flag set and when
445      * the offline slave is remerge()'d, it is deleted from the slave and
446      * delete() is called again.
447      *
448      * Trying to lookupFile() or getFile() a deleted file throws
449      * FileNotFoundException.
450      */

451     public void delete() {
452         logger.debug("delete(" + getPath() + ")");
453         _isDeleted = true;
454         _link = null;
455         if (isDirectory()) {
456             for (Iterator JavaDoc iter = getFiles().iterator(); iter.hasNext();) {
457                 LinkedRemoteFileInterface myFile =
458                     (LinkedRemoteFileInterface) iter.next();
459                 myFile.delete();
460             }
461             try {
462                 if (dirSize() == 0) { //remove empty dir
463
Object JavaDoc ret = getParentFile().getMap().remove(getName());
464                     if (ret == null)
465                         throw new NullPointerException JavaDoc();
466                 }
467             } catch (FileNotFoundException JavaDoc ex) {
468                 logger.log(
469                     Level.FATAL,
470                     "FileNotFoundException on getParentFile()",
471                     ex);
472             }
473             return;
474         } else {
475             synchronized (_slaves) {
476                 for (Iterator JavaDoc iter = _slaves.iterator(); iter.hasNext();) {
477                     RemoteSlave rslave = (RemoteSlave) iter.next();
478                     Slave slave;
479                     try {
480                         slave = rslave.getSlave();
481                     } catch (SlaveUnavailableException ex) {
482                         logger.info("slave not available for deletion");
483                         continue;
484                     }
485                     try {
486                         System.out.println(
487                             "DELETE: " + rslave.getName() + ": " + getPath());
488                         slave.delete(getPath());
489                         // throws RemoteException, IOException
490
iter.remove();
491                     } catch (FileNotFoundException JavaDoc ex) {
492                         iter.remove();
493                         logger.warn(
494                             getPath()
495                                 + " missing on "
496                                 + rslave.getName()
497                                 + " during delete, assumed deleted",
498                             ex);
499                     } catch (RemoteException JavaDoc ex) {
500                         rslave.handleRemoteException(ex);
501                         continue;
502                     } catch (IOException JavaDoc ex) {
503                         logger.log(
504                             Level.FATAL,
505                             "IOException deleting file on slave "
506                                 + rslave.getName(),
507                             ex);
508                         continue;
509                     }
510                 }
511             }
512
513             if (_slaves.isEmpty()) {
514                 //remove slaveless file
515
try {
516                     getParentFile().getMap().remove(getName());
517                     getParentFileNull().addSize(-length());
518                 } catch (FileNotFoundException JavaDoc ex) {
519                     logger.log(
520                         Level.FATAL,
521                         "FileNotFoundException on getParentFile()",
522                         ex);
523                 }
524             } else {
525                 logger.log(
526                     Level.INFO,
527                     getPath()
528                         + " queued for deletion, remaining slaves:"
529                         + _slaves);
530             }
531         }
532     }
533
534     public void deleteOthers(Set JavaDoc destSlaves) {
535         synchronized (_slaves) {
536             for (Iterator JavaDoc iter = _slaves.iterator(); iter.hasNext();) {
537                 RemoteSlave tempSlave = (RemoteSlave) iter.next();
538                 if (destSlaves.contains(tempSlave))
539                     continue; // do not want to delete the archived file
540
// delete files off of slaves not in destSlaves
541
try {
542                     tempSlave.getSlave().delete(getPath());
543                     iter.remove();
544                 } catch (RemoteException JavaDoc e) {
545                     tempSlave.handleRemoteException(e);
546                 } catch (FileNotFoundException JavaDoc ex) {
547                     logger.warn(
548                         getPath()
549                             + " missing on "
550                             + tempSlave.getName()
551                             + " during delete, assumed deleted",
552                         ex);
553                     iter.remove();
554                 } catch (SlaveUnavailableException e) {
555                     logger.debug("Unable to delete file on offline slave", e);
556                 } catch (IOException JavaDoc e) {
557                     logger.debug("IOException deleting file from slave", e);
558                 }
559             }
560         }
561     }
562     public long dirSize() {
563         if (_files == null)
564             throw new IllegalStateException JavaDoc("Cannot be called on a non-directory");
565         return _files.size();
566     }
567
568     public boolean equals(Object JavaDoc obj) {
569         if (obj instanceof LinkedRemoteFileInterface
570             && ((LinkedRemoteFileInterface) obj).getPath().equals(getPath())) {
571             return true;
572         }
573         return false;
574     }
575
576     public Collection JavaDoc getAvailableSlaves() throws NoAvailableSlaveException {
577         ArrayList JavaDoc availableSlaves = new ArrayList JavaDoc();
578         for (Iterator JavaDoc iter = getSlaves().iterator(); iter.hasNext();) {
579             RemoteSlave rslave = (RemoteSlave) iter.next();
580             if (!rslave.isAvailable())
581                 continue;
582             availableSlaves.add(rslave);
583         }
584         if (availableSlaves.isEmpty()) {
585             throw new NoAvailableSlaveException(
586                 getPath() + " has 0 slaves online");
587         }
588         return availableSlaves;
589     }
590
591     /**
592      * Uses cached checksum if the cached checksum is not 0
593      */

594     public long getCheckSum() throws NoAvailableSlaveException {
595         if (_checkSum == 0 && _length != 0) {
596             _checkSum = getCheckSumFromSlave();
597             if (_checkSum == 0)
598                 throw new NoAvailableSlaveException(
599                     "Could not find a slave to check crc of " + getPath());
600         }
601         return _checkSum;
602     }
603
604     /**
605      * Returns the cached checksum or 0 if no checksum was cached.
606      * <p>
607      * Use {getCheckSum()} to automatically calculate checksum if no cached
608      * checksum is available.
609      */

610     public long getCheckSumCached() {
611         return _checkSum;
612     }
613
614     /**
615      * Returns 0 if the checksum cannot be read.
616      */

617     public long getCheckSumFromSlave() {
618         try {
619             for (Iterator JavaDoc iter = getAvailableSlaves().iterator();
620                 iter.hasNext();
621                 ) {
622                 RemoteSlave slave = (RemoteSlave) iter.next();
623                 try {
624                     _checkSum = slave.getSlave().checkSum(getPath());
625                 } catch (RemoteException JavaDoc e) {
626                     continue;
627                 } catch (IOException JavaDoc e) {
628                     continue;
629                 } catch (SlaveUnavailableException e) {
630                     continue;
631                 }
632                 return _checkSum;
633             }
634         } catch (NoAvailableSlaveException e) {
635             return 0;
636         }
637         return 0;
638     }
639
640     public Collection JavaDoc getDirectories() {
641         Collection JavaDoc temp = getFiles();
642         for (Iterator JavaDoc iter = temp.iterator(); iter.hasNext();) {
643             if (((LinkedRemoteFileInterface) iter.next()).isFile())
644                 iter.remove();
645         }
646         return temp;
647     }
648
649     /**
650      * Returns fileName contained in this directory.
651      *
652      * @param fileName
653      * @throws FileNotFoundException
654      * if fileName doesn't exist in the files Map
655      */

656     public LinkedRemoteFileInterface getFile(String JavaDoc fileName)
657         throws FileNotFoundException JavaDoc {
658         LinkedRemoteFileInterface file = getFileDeleted(fileName);
659         if (file.isDeleted())
660             throw new QueuedDeletionException("File is queued for deletion");
661         return file;
662     }
663
664     public LinkedRemoteFileInterface getFileDeleted(String JavaDoc fileName)
665         throws FileNotFoundException JavaDoc {
666         LinkedRemoteFileInterface file =
667             (LinkedRemoteFileInterface) _files.get(fileName);
668         if (file == null)
669             throw new FileNotFoundException JavaDoc(
670                 "No such file or directory: " + fileName);
671         return file;
672     }
673
674     /**
675      * Returns a Collection of all the LinkedRemoteFile objects in this
676      * directory, with all .isDeleted() files removed.
677      *
678      * The Collection can be safely modified, it is a copy.
679      *
680      * @return a Collection of all the LinkedRemoteFile objects in this
681      * directory, with all .isDeleted() files removed.
682      */

683     public Collection JavaDoc getFiles() {
684         if (_files == null)
685             throw new IllegalStateException JavaDoc("Isn't a directory");
686         return getFilesMap().values();
687     }
688
689     /**
690      * Returns a map for this directory, having String name as key and
691      * LinkedRemoteFile file as value, with all .isDeleted() files removed.
692      *
693      * The Map can be safely modified, it is a copy.
694      *
695      * @return map for this directory, having String name as key and
696      * LinkedRemoteFile file as value, with all .isDeleted() files
697      * removed.
698      */

699     private Map JavaDoc getFilesMap() {
700         Hashtable JavaDoc ret = new Hashtable JavaDoc(_files);
701
702         for (Iterator JavaDoc iter = ret.values().iterator(); iter.hasNext();) {
703             LinkedRemoteFileInterface file =
704                 (LinkedRemoteFileInterface) iter.next();
705             if (file.isDeleted())
706                 iter.remove();
707         }
708         return ret;
709     }
710
711     public String JavaDoc getGroupname() {
712         if (_group == null || _group.equals(""))
713             return "drftpd";
714         return _group;
715     }
716
717     public RemoteFileInterface getLink() throws FileNotFoundException JavaDoc {
718         return lookupFile(getLinkPath());
719     }
720
721     public String JavaDoc getLinkPath() {
722         return _link;
723     }
724
725     /**
726      * Returns the underlying Map for this directory.
727      *
728      * It is dangerous to modify without knowing what you're doing. Dirsize
729      * needs to be taken into account as well as sending approperiate commands
730      * to the slaves.
731      *
732      * @return the underlying Map for this directory.
733      */

734     public Map JavaDoc getMap() {
735         return _files;
736     }
737
738     public String JavaDoc getName() {
739         return _name;
740     }
741
742     public LinkedRemoteFileInterface getOldestFile()
743         throws ObjectNotFoundException {
744         Iterator JavaDoc iter = getFiles().iterator();
745         if (!iter.hasNext())
746             throw new ObjectNotFoundException();
747         LinkedRemoteFile oldestFile = (LinkedRemoteFile) iter.next();
748         for (; iter.hasNext();) {
749             LinkedRemoteFile file = (LinkedRemoteFile) iter.next();
750             if (oldestFile.lastModified() > file.lastModified()) {
751                 oldestFile = file;
752             }
753         }
754         return oldestFile;
755     }
756
757     public String JavaDoc getParent() throws FileNotFoundException JavaDoc {
758         return getParentFile().getPath();
759     }
760
761     public LinkedRemoteFile getParentFile() throws FileNotFoundException JavaDoc {
762         if (_parent == null)
763             throw new FileNotFoundException JavaDoc("root directory has no parent");
764         return _parent;
765     }
766
767     public LinkedRemoteFile getParentFileNull() {
768         return _parent;
769     }
770
771     public String JavaDoc getPath() {
772         StringBuffer JavaDoc path = new StringBuffer JavaDoc();
773         LinkedRemoteFileInterface parent = this;
774
775         while (true) {
776             if (parent.getName().length() == 0)
777                 break;
778             path.insert(0, "/" + parent.getName());
779             try {
780                 parent = parent.getParentFile();
781                 // throws FileNotFoundException
782
} catch (FileNotFoundException JavaDoc ex) {
783                 break;
784             }
785         }
786         if (path.length() == 0)
787             return "/";
788         return path.toString();
789     }
790
791     public LinkedRemoteFile getRoot() {
792         LinkedRemoteFile root = this;
793         try {
794             while (true)
795                 root = root.getParentFile();
796         } catch (FileNotFoundException JavaDoc ex) {
797             return root;
798         }
799     }
800
801     public synchronized SFVFile getSFVFile()
802         throws IOException JavaDoc, FileNotFoundException JavaDoc, NoAvailableSlaveException {
803
804         if (sfvFile == null) {
805             while (true) {
806                 RemoteSlave rslave =
807                     _ftpConfig
808                         .getSlaveManager()
809                         .getSlaveSelectionManager()
810                         .getASlaveForMaster(
811                         this,
812                         _ftpConfig);
813                 try {
814                     sfvFile = rslave.getSlave().getSFVFile(getPath());
815                     sfvFile.setCompanion(this);
816                     break;
817                 } catch (RemoteException JavaDoc ex) {
818                     rslave.handleRemoteException(ex);
819                 } catch (SlaveUnavailableException e) {
820                     continue;
821                 }
822             }
823         }
824         if (sfvFile.size() == 0) {
825             throw new FileNotFoundException JavaDoc("sfv file contains no checksum entries");
826         }
827         return sfvFile;
828     }
829
830     /**
831      * returns slaves. returns null if a directory.
832      */

833     public Collection JavaDoc getSlaves() {
834         if (_slaves == null)
835             throw new IllegalStateException JavaDoc("getSlaves() called on a directory");
836         return _slaves;
837     }
838
839     public String JavaDoc getUsername() {
840         if (_owner == null || _owner.equals(""))
841             return "nobody";
842         return _owner;
843     }
844
845     public long getXferspeed() {
846         if (getXfertime() == 0)
847             return 0;
848         return (length() / getXfertime());
849     }
850
851     /**
852      * @return xfertime in milliseconds
853      */

854     public long getXfertime() {
855         return _xfertime;
856     }
857
858     /**
859      * Returns true if this directory contains a file named filename, this is
860      * case sensitive.
861      *
862      * @param filename
863      * The name of the file
864      * @return true if this directory contains a file named filename, this is
865      * case sensitive.
866      */

867     public boolean hasFile(String JavaDoc filename) {
868         return _files.containsKey(filename);
869     }
870
871     public int hashCode() {
872         return getName().hashCode();
873     }
874
875     /**
876      * Returns true if this file or directory uses slaves that are currently
877      * offline.
878      *
879      * @return true if this file or directory uses slaves that are currently
880      * offline.
881      */

882     public boolean hasOfflineSlaves() {
883         if (isFile()) {
884             for (Iterator JavaDoc iter = getSlaves().iterator(); iter.hasNext();) {
885                 RemoteSlave rslave = (RemoteSlave) iter.next();
886                 if (rslave == null)
887                     throw new NullPointerException JavaDoc();
888                 if (!rslave.isAvailable())
889                     return true;
890             }
891         } else if (isDirectory()) {
892             for (Iterator JavaDoc iter = getFiles().iterator(); iter.hasNext();) {
893                 if (((LinkedRemoteFileInterface) iter.next())
894                     .hasOfflineSlaves())
895                     return true;
896             }
897         }
898         return false;
899     }
900
901     public boolean hasSlave(RemoteSlave slave) {
902         return _slaves.contains(slave);
903     }
904
905     /**
906      * Does file have online slaves?
907      *
908      * @return Always true for directories
909      */

910     public boolean isAvailable() {
911         if (isDirectory())
912             return true;
913         for (Iterator JavaDoc iter = getSlaves().iterator(); iter.hasNext();) {
914             RemoteSlave rslave = (RemoteSlave) iter.next();
915             if (rslave == null)
916                 throw new RuntimeException JavaDoc();
917             if (rslave.isAvailable())
918                 return true;
919         }
920         return false;
921     }
922
923     /**
924      * Returns true if this file is queued for deletion.
925      *
926      * @return true if this file is queued for deletion.
927      */

928     public boolean isDeleted() {
929         return _isDeleted;
930     }
931
932     public boolean isDirectory() {
933         return !isFile();
934     }
935
936     /**
937      * @return true if directory is empty.
938      */

939     private boolean isEmpty() {
940         if (_files == null)
941             throw new IllegalStateException JavaDoc();
942         return _files.isEmpty();
943     }
944
945     public boolean isFile() {
946         return _files == null && _slaves != null;
947     }
948
949     /**
950      * isLink() && isDeleted() means queued rename, target is getLink().
951      */

952     public boolean isLink() {
953         return _link != null;
954     }
955
956     public long lastModified() {
957         return _lastModified;
958     }
959
960     public long length() {
961         // if (isDirectory()) {
962
// long length = 0;
963
// for (Iterator iter = getFiles().iterator(); iter.hasNext();) {
964
// length += ((LinkedRemoteFile) iter.next()).length();
965
// }
966
// if (_length != 0 && length != _length)
967
// logger.warn(
968
// "",
969
// new Throwable(
970
// "Cached checksum missmatch: "
971
// + length
972
// + " != "
973
// + _length
974
// + " for "
975
// + toString()));
976
// _length = length;
977
// return length;
978
// }
979
if (_length < 0)
980             return 0;
981         return _length;
982     }
983
984     public LinkedRemoteFile lookupFile(String JavaDoc path)
985         throws FileNotFoundException JavaDoc {
986         return lookupFile(path, false);
987     }
988
989     public LinkedRemoteFile lookupFile(String JavaDoc path, boolean includeDeleted)
990         throws FileNotFoundException JavaDoc {
991
992         NonExistingFile ret = lookupNonExistingFile(path, includeDeleted);
993
994         if (ret.hasPath())
995             throw new FileNotFoundException JavaDoc(path + ": File not found");
996         return (LinkedRemoteFile) ret.getFile();
997     }
998
999     public NonExistingFile lookupNonExistingFile(String JavaDoc path) {
1000        return lookupNonExistingFile(path, false);
1001    }
1002
1003    /**
1004     * @param includeDeleted Whether to look up deleted files aswell.
1005     */

1006    public NonExistingFile lookupNonExistingFile(
1007        String JavaDoc path,
1008        boolean includeDeleted) {
1009        if (path == null)
1010            throw new IllegalArgumentException JavaDoc("null path not allowed");
1011        LinkedRemoteFile currFile = this;
1012
1013        if (path.charAt(0) == '/')
1014            currFile = getRoot();
1015
1016        //check for leading ~
1017
if (path.length() == 1 && path.equals("~")) {
1018            currFile = getRoot();
1019            path = "";
1020        } else if (path.length() >= 2 && path.substring(0, 2).equals("~/")) {
1021            currFile = getRoot();
1022            path = path.substring(2);
1023        }
1024
1025        StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(path, "/");
1026        while (st.hasMoreTokens()) {
1027            String JavaDoc currFileName = st.nextToken();
1028            if (currFileName.equals("."))
1029                continue;
1030            if (currFileName.equals("..")) {
1031                try {
1032                    currFile = currFile.getParentFile();
1033                } catch (FileNotFoundException JavaDoc ex) {
1034                }
1035                continue;
1036            }
1037            LinkedRemoteFile nextFile;
1038            try {
1039                if (includeDeleted) {
1040                    nextFile =
1041                        (LinkedRemoteFile) currFile.getFileDeleted(
1042                            currFileName);
1043                } else {
1044                    nextFile =
1045                        (LinkedRemoteFile) currFile.getFileDeleted(
1046                            currFileName);
1047                }
1048            } catch (FileNotFoundException JavaDoc ex) {
1049                StringBuffer JavaDoc remaining = new StringBuffer JavaDoc(currFileName);
1050                if (st.hasMoreElements()) {
1051                    remaining.append('/').append(st.nextToken(""));
1052                }
1053                return new NonExistingFile(currFile, remaining.toString());
1054            }
1055            currFile = nextFile;
1056        }
1057        return new NonExistingFile(currFile, null);
1058    }
1059
1060    /**
1061     * Returns path for a non-existing file. Performs path normalization and
1062     * returns an absolute path
1063     */

1064    public String JavaDoc lookupPath(String JavaDoc path) {
1065        NonExistingFile ret = lookupNonExistingFile(path);
1066        if (!ret.hasPath()) {
1067            return ret.getFile().getPath();
1068        }
1069        return ret.getFile().getPath() + '/' + ret.getPath();
1070    }
1071
1072    public SFVFile lookupSFVFile()
1073        throws IOException JavaDoc, FileNotFoundException JavaDoc, NoAvailableSlaveException {
1074        if (!isDirectory())
1075            throw new IllegalStateException JavaDoc("lookupSFVFile must be called on a directory");
1076
1077        for (Iterator JavaDoc iter = getFiles().iterator(); iter.hasNext();) {
1078            LinkedRemoteFileInterface myFile =
1079                (LinkedRemoteFileInterface) iter.next();
1080            if (myFile.getName().toLowerCase().endsWith(".sfv")) {
1081                return myFile.getSFVFile();
1082            }
1083        }
1084        throw new FileNotFoundException JavaDoc("no sfv file in directory");
1085    }
1086
1087    /**
1088     * Use addFile() if you want lastModified to be updated.
1089     */

1090    public LinkedRemoteFile putFile(RemoteFileInterface file) {
1091        return putFile(file, file.getName());
1092    }
1093
1094    /**
1095     * @param torslave
1096     * RemoteSlave to replicate to.
1097     */

1098    // public void replicate(final RemoteSlave torslave)
1099
// throws NoAvailableSlaveException, IOException {
1100
//
1101
// final RemoteSlave fromslave = getASlaveForDownload();
1102
// Transfer fromtransfer = fromslave.getSlave().listen(false);
1103
// final Transfer totransfer =
1104
// torslave.getSlave().connect(
1105
// new InetSocketAddress(
1106
// fromslave.getInetAddress(),
1107
// fromtransfer.getLocalPort()),
1108
// false);
1109
//
1110
// Thread t = new Thread(new Runnable() {
1111
// public void run() {
1112
// try {
1113
// totransfer.receiveFile(
1114
// getParentFile().getPath(),
1115
// 'I',
1116
// getName(),
1117
// 0L);
1118
// } catch (RemoteException e) {
1119
// torslave.handleRemoteException(e);
1120
// logger.warn(EMPTY_STRING, e);
1121
// } catch (FileNotFoundException e) {
1122
// throw new FatalException(e);
1123
// } catch (IOException e) {
1124
// throw new RuntimeException(e);
1125
// }
1126
// }
1127
// });
1128
// t.start();
1129
// fromtransfer.sendFile(getPath(), 'I', 0, false);
1130
// }
1131

1132    /**
1133     * @param toName
1134     * name argument to LinkedRemoteFile constructor
1135     */

1136    private LinkedRemoteFile putFile(RemoteFileInterface file, String JavaDoc toName) {
1137        if (_files.containsKey(toName)) {
1138            if (((LinkedRemoteFileInterface) _files.get(toName)).isDeleted()) {
1139                throw new IllegalStateException JavaDoc(
1140                    "Don't overwrite! "
1141                        + getPath()
1142                        + " "
1143                        + toName
1144                        + " (is queued for deletion)");
1145            } else {
1146                throw new IllegalStateException JavaDoc(
1147                    "Don't overwrite! " + getPath() + " " + toName);
1148            }
1149        }
1150        //validate
1151
if (file.isFile()) {
1152            if (file.getSlaves() == null)
1153                throw new RuntimeException JavaDoc(file.toString());
1154            for (Iterator JavaDoc iter = file.getSlaves().iterator();
1155                iter.hasNext();
1156                ) {
1157                if (iter.next() == null)
1158                    throw new RuntimeException JavaDoc();
1159            }
1160        }
1161
1162        //the constructor takes care of addSize()
1163
LinkedRemoteFile linkedfile =
1164            new LinkedRemoteFile(this, file, toName, _ftpConfig);
1165        _files.put(linkedfile.getName(), linkedfile);
1166        return linkedfile;
1167    }
1168
1169    private void queueRename(LinkedRemoteFile toFile) {
1170        _link = toFile.getPath();
1171        _isDeleted = true;
1172    }
1173
1174    public TransferStatus receiveFile(
1175        Transfer transfer,
1176        char type,
1177        long offset)
1178        throws IOException JavaDoc {
1179        return transfer.receiveFile(getParent(), type, getName(), offset);
1180    }
1181
1182    /**
1183     * Merges mergedir directory onto <code>this</code> directories. If
1184     * duplicates exist, the slaves are added to this object and the
1185     * file-attributes of the oldest file (lastModified) are kept.
1186     */

1187    public void remerge(LinkedRemoteFile mergedir, RemoteSlave rslave) {
1188        if (_ftpConfig == null)
1189            throw new IllegalStateException JavaDoc("_ftpConfig == null");
1190        remergePass1(mergedir, rslave);
1191        remergePass2(mergedir, rslave);
1192    }
1193
1194    private void remergePass1(LinkedRemoteFile mergedir, RemoteSlave rslave) {
1195        if (!isDirectory())
1196            throw new IllegalArgumentException JavaDoc("merge() called on a non-directory");
1197
1198        if (!mergedir.isDirectory())
1199            throw new IllegalArgumentException JavaDoc("argument is not a directory");
1200
1201        // add/merge all files from mergedir
1202
for (Iterator JavaDoc i = mergedir.getFiles().iterator(); i.hasNext();) {
1203            LinkedRemoteFile slavefile = (LinkedRemoteFile) i.next();
1204            if (slavefile.isDirectory() && slavefile.length() == 0) {
1205                logger.fatal(
1206                    "Attempt to add empty directory: "
1207                        + slavefile
1208                        + " from "
1209                        + rslave.getName());
1210                continue;
1211            }
1212            //this is localdir
1213
//localfile is local file
1214
//mergedir is mergedir argument to remerge()
1215
//mergefile is file to be merged
1216
LinkedRemoteFile localfile =
1217                (LinkedRemoteFile) _files.get(slavefile.getName());
1218            // two scenarios: localfile does/doesn't exist.
1219
if (localfile == null) {
1220                // local file does not exist, just put it in the hashtable
1221
recursiveSetRSlaveAndConfig(slavefile, _ftpConfig, rslave);
1222                slavefile._parent = this;
1223                _files.put(slavefile.getName(), slavefile);
1224                logger.info(
1225                    slavefile.getPath() + " added from " + rslave.getName());
1226            } else { //file exists
1227
if (localfile.isDeleted()) {
1228                    //logger.debug("localfile isDeleted() " + localfile);
1229
//// queued delete or rename ////
1230
if (localfile.isLink()) {
1231                        String JavaDoc linktarget = localfile.getLinkPath();
1232                        //// rename ////
1233
try {
1234                            LinkedRemoteFileInterface renameTo =
1235                                (LinkedRemoteFileInterface) localfile.getLink();
1236                            logger.debug(
1237                                "queued rename for "
1238                                    + localfile.getPath()
1239                                    + " to "
1240                                    + renameTo.getPath());
1241                            try {
1242                                rslave.getSlave().rename(
1243                                    localfile.getPath(),
1244                                    renameTo.getParent(),
1245                                    renameTo.getName());
1246                                //recusive migration of rslaves from source to
1247
// dest
1248
if (localfile.isFile()) {
1249                                    recursiveRenameLoopFile(
1250                                        localfile,
1251                                        (LinkedRemoteFile) renameTo);
1252                                } else {
1253                                    recursiveRenameLoop(
1254                                        localfile,
1255                                        (LinkedRemoteFile) renameTo);
1256                                }
1257
1258                                //migrate mergefile on mergedir or files will
1259
// be removed cause they aren't in slave
1260
// filelist
1261
//simple ugly move of the lrf object so that
1262
// remergePass2() won't delete it
1263
{
1264                                    NonExistingFile ret =
1265                                        slavefile.lookupNonExistingFile(
1266                                            linktarget);
1267                                    LinkedRemoteFile destDir = ret.getFile();
1268                                    if (!ret.hasPath())
1269                                        throw new RuntimeException JavaDoc(
1270                                            ret
1271                                                + " - target already exists on the slave - ???");
1272                                    StringTokenizer JavaDoc destst =
1273                                        new StringTokenizer JavaDoc(ret.getPath(), "/");
1274                                    String JavaDoc desttok = null;
1275                                    if (!destst.hasMoreTokens())
1276                                        throw new RuntimeException JavaDoc("Invalid queued rename target");
1277                                    while (destst.hasMoreTokens()) {
1278                                        desttok = destst.nextToken();
1279                                        if (destst.hasMoreTokens()) {
1280                                            destDir =
1281                                                destDir.createDirectory(
1282                                                    "drftpd",
1283                                                    "drftpd",
1284                                                    desttok);
1285                                            logger.debug(
1286                                                "Created " + destDir.getPath());
1287                                        }
1288                                    }
1289                                    mergedir._files.remove(slavefile.getName());
1290                                    slavefile._name = desttok;
1291                                    destDir.putFile(slavefile);
1292                                    slavefile._parent = destDir;
1293                                    logger.debug(
1294                                        "Renamed "
1295                                            + slavefile.getName()
1296                                            + " to "
1297                                            + destDir.getPath());
1298                                    //remove source
1299
}
1300
1301                                //file.removeSlave(rslave);
1302
//renameTo.addSlave(rslave);
1303
} catch (RemoteException JavaDoc e) {
1304                                rslave.handleRemoteException(e);
1305                            } catch (SlaveUnavailableException e) {
1306                                throw new RuntimeException JavaDoc(e);
1307                            } catch (IOException JavaDoc e) {
1308                                logger.warn("", e);
1309                            }
1310                            continue;
1311                        } catch (FileNotFoundException JavaDoc e) {
1312                            //localfile.getLink() failed
1313
//target doesn't exist, queued delete instead
1314
logger.info(
1315                                localfile.getPath()
1316                                    + " target didn't exist for queued rename, scheduling for deletion");
1317                            localfile._link = null;
1318                        }
1319                    }
1320
1321                    //// delete ////
1322
if (!localfile.isLink()) {
1323                        logger.log(
1324                            Level.WARN,
1325                            "Queued delete on "
1326                                + rslave
1327                                + " for file "
1328                                + slavefile);
1329                        if (!slavefile.isDirectory())
1330                            slavefile.addSlave(rslave);
1331                        slavefile.delete();
1332                        localfile.delete();
1333                        continue;
1334                        //TODO subdir contains queued for rename files.
1335
}
1336                } // end queued del/ren
1337

1338                if (slavefile.isFile()
1339                    && localfile.length() != slavefile.length()) {
1340                    //// conflict ////
1341
Collection JavaDoc filerslaves = slavefile.getSlaves();
1342
1343                    if ((filerslaves.size() == 1
1344                        && filerslaves.contains(rslave))
1345                        || localfile.length() == 0) {
1346                        //we're the only slave with the file.
1347
localfile.setLength(slavefile.length());
1348                        localfile.setCheckSum(0L);
1349                        localfile.setLastModified(slavefile.lastModified());
1350                    } else if (slavefile.length() == 0) {
1351                        logger.log(
1352                            Level.INFO,
1353                            "Deleting conflicting 0byte "
1354                                + slavefile
1355                                + " on "
1356                                + rslave);
1357                        try {
1358                            rslave.getSlave().delete(slavefile.getPath());
1359                        } catch (PermissionDeniedException ex) {
1360                            logger.log(
1361                                Level.FATAL,
1362                                "Error deleting 0byte file on " + rslave,
1363                                ex);
1364                        } catch (Exception JavaDoc e) {
1365                            throw new FatalException(e);
1366                        }
1367                        continue;
1368                    } else {
1369                        //// conflict, rename file... ////
1370
try {
1371                            rslave.getSlave().rename(
1372                                getPath() + "/" + slavefile.getName(),
1373                                getPath(),
1374                                slavefile.getName()
1375                                    + "."
1376                                    + rslave.getName()
1377                                    + ".conflict");
1378                            mergedir._files.remove(slavefile.getName());
1379                            slavefile._name =
1380                                slavefile.getName()
1381                                    + "."
1382                                    + rslave.getName()
1383                                    + ".conflict";
1384                            // this is enough so it won't be deleted in remergePass2()
1385
mergedir._files.put(slavefile.getName(), slavefile);
1386                            slavefile.addSlave(rslave);
1387                            slavefile._parent = this;
1388                            _files.put(slavefile.getName(), slavefile);
1389                            logger.log(
1390                                Level.WARN,
1391                                "2 or more slaves contained same file with different sizes, renamed to "
1392                                    + slavefile.getName());
1393                            continue;
1394                        } catch (Exception JavaDoc e) {
1395                            throw new FatalException(e);
1396                        }
1397                    }
1398                }
1399
1400                // 4 scenarios: new/existing file/directory
1401
if (slavefile.isDirectory()) {
1402                    if (!localfile.isDirectory())
1403                        throw new RuntimeException JavaDoc(
1404                            "!!! ERROR: Directory/File conflict: "
1405                                + slavefile
1406                                + " and "
1407                                + localfile
1408                                + " from "
1409                                + rslave.getName());
1410                    // is a directory -- dive into directory and start merging
1411
localfile.remergePass1(slavefile, rslave);
1412                } else {
1413                    if (!slavefile.isFile())
1414                        throw new RuntimeException JavaDoc();
1415                    if (localfile.isDirectory())
1416                        throw new RuntimeException JavaDoc(
1417                            "!!! ERROR: File/Directory conflict: "
1418                                + slavefile
1419                                + " and "
1420                                + localfile
1421                                + " from "
1422                                + rslave.getName());
1423                    localfile.addSlave(rslave);
1424                }
1425            } // file != null
1426
}
1427
1428    }
1429
1430    private void remergePass2(LinkedRemoteFile mergedir, RemoteSlave rslave) {
1431        synchronized (_files) {
1432            // remove all slaves not in mergedir.getFiles()
1433
// unmerge() gets called on all files not on slave & all directories
1434
//for (Iterator i = new ArrayList(getFilesMap().values()).iterator();
1435
// getFilesMap() returns a copy of the list and without isDeleted files
1436
for (Iterator JavaDoc i = new ArrayList JavaDoc(_files.values()).iterator();
1437                i.hasNext();
1438                ) {
1439                LinkedRemoteFile file = (LinkedRemoteFile) i.next();
1440                if (mergedir == null) { // slave doesn't have the directory
1441
if (file.isFile()) {
1442                        file.unmergeFile(rslave);
1443                    } else {
1444                        file.remergePass2(null, rslave);
1445                    }
1446                    continue;
1447                }
1448                if (!mergedir.hasFile(file.getName())) {
1449                    if (file.isFile()) {
1450                        file.unmergeFile(rslave);
1451                    } else {
1452                        file.remergePass2(null, rslave);
1453                    }
1454                } else {
1455                    if (file.isDirectory()) {
1456                        try {
1457                            file.remergePass2(
1458                                (LinkedRemoteFile) mergedir.getFile(
1459                                    file.getName()),
1460                                rslave);
1461                        } catch (FileNotFoundException JavaDoc e) {
1462                            throw new RuntimeException JavaDoc(
1463                                "inconsistent with hasFile() above",
1464                                e);
1465                        }
1466                    }
1467                    // else slave has the file
1468
}
1469            }
1470        }
1471    }
1472
1473    /**
1474     * Shorthand for _slaves.remove() that checks if _slaves is empty and calls .delete() if it is.
1475     */

1476    public boolean removeSlave(RemoteSlave slave) {
1477        if (_slaves == null)
1478            throw new IllegalStateException JavaDoc("Cannot removeSlave() on directory");
1479        boolean ret = _slaves.remove(slave);
1480        if (_slaves.isEmpty())
1481            delete();
1482        return ret;
1483    }
1484
1485    /**
1486     * Renames this file
1487     */

1488    public LinkedRemoteFile renameTo(String JavaDoc toDirPath, String JavaDoc toName)
1489        throws IOException JavaDoc, FileNotFoundException JavaDoc {
1490        if (toDirPath.charAt(0) != '/')
1491            throw new RuntimeException JavaDoc("renameTo() must be given an absolute path as argument");
1492        if (toName.indexOf('/') != -1)
1493            throw new RuntimeException JavaDoc("Cannot rename to non-existing directory");
1494        if (_ftpConfig == null)
1495            throw new RuntimeException JavaDoc("_ftpConfig is null: " + this);
1496
1497        LinkedRemoteFile toDir = lookupFile(toDirPath);
1498        // throws FileNotFoundException
1499
{
1500            LinkedRemoteFile tmpDir = toDir;
1501            do {
1502                if (tmpDir == this) {
1503                    throw new IOException JavaDoc("Cannot rename into a subdirectory of self");
1504                }
1505            } while ((tmpDir = tmpDir.getParentFileNull()) != null);
1506        }
1507
1508        //slaves are copied here too...
1509
LinkedRemoteFile toFile = toDir.putFile(this, toName);
1510
1511        if (!toFile.isDirectory())
1512            toFile._slaves = Collections.synchronizedList(new ArrayList JavaDoc());
1513        queueRename(toFile);
1514        if (isDirectory()) {
1515            for (Iterator JavaDoc iter =
1516                _ftpConfig.getSlaveManager().getSlaves().iterator();
1517                iter.hasNext();
1518                ) {
1519                RemoteSlave rslave = (RemoteSlave) iter.next();
1520                Slave slave;
1521                try {
1522                    slave = rslave.getSlave();
1523                } catch (SlaveUnavailableException e) {
1524                    //trust that hasOfflineSlaves() did a good job and no
1525
// files are present on offline slaves
1526
continue;
1527                }
1528                try {
1529                    slave.rename(getPath(), toDirPath, toName);
1530                } catch (RemoteException JavaDoc ex) {
1531                    rslave.handleRemoteException(ex);
1532                } catch (IOException JavaDoc ex) {
1533                    logger.log(
1534                        Level.FATAL,
1535                        "IOException in renameTo() for dir for "
1536                            + rslave.getName(),
1537                        ex);
1538                }
1539            }
1540            recursiveRenameLoop(this, toFile);
1541        } else {
1542            for (Iterator JavaDoc iter = new ArrayList JavaDoc(getSlaves()).iterator();
1543                iter.hasNext();
1544                ) {
1545                RemoteSlave rslave = (RemoteSlave) iter.next();
1546                Slave slave;
1547                try {
1548                    slave = rslave.getSlave();
1549                } catch (SlaveUnavailableException ex) {
1550                    continue;
1551                }
1552                try {
1553                    slave.rename(getPath(), toDirPath, toName);
1554                    removeSlave(rslave);
1555                    toFile.addSlave(rslave);
1556                    // throws RemoteException, IOException
1557
} catch (RemoteException JavaDoc ex) {
1558                    rslave.handleRemoteException(ex);
1559                } catch (IOException JavaDoc ex) {
1560                    logger.log(
1561                        Level.FATAL,
1562                        "IO error from "
1563                            + rslave.getName()
1564                            + " on a file in LinkedRemoteFile",
1565                        ex);
1566                }
1567            }
1568        }
1569
1570        //Object[] ret = lookupNonExistingFile(to);
1571
//
1572
//String toName = (String) ret[1];
1573
// if (toName == null)
1574
// throw new ObjectExistsException("Target already exists");
1575

1576        // try {
1577
// getParentFile()._files.remove(fromName);
1578
// getParentFileNull().addSize(-length());
1579
// } catch (FileNotFoundException ex) {
1580
// logger.log(
1581
// Level.FATAL,
1582
// "FileNotFoundException on getParentFile() on 'this' in rename",
1583
// ex);
1584
// }
1585

1586        // _parent = toDir;
1587
// toDir.getMap().put(toName, this);
1588
// toDir.addSize(length());
1589
// _name = toName;
1590
return toFile;
1591    }
1592    public TransferStatus sendFile(Transfer transfer, char type, long offset)
1593        throws IOException JavaDoc {
1594        return transfer.sendFile(getPath(), type, offset);
1595    }
1596
1597    public void setCheckSum(long checkSum) {
1598        _checkSum = checkSum;
1599    }
1600
1601    /**
1602     * Used for resurrecting deleted directories.
1603     */

1604    public void setDeleted(boolean b) {
1605        _isDeleted = b;
1606    }
1607    public void setGroup(String JavaDoc group) {
1608        _group = group != null ? group.intern() : null;
1609    }
1610
1611    public void setLastModified(long lastModified) {
1612        _lastModified = lastModified;
1613    }
1614
1615    public void setLength(long length) {
1616        getParentFileNull().addSize(length - _length);
1617        _length = length;
1618    }
1619    public void setOwner(String JavaDoc owner) {
1620        _owner = owner != null ? owner.intern() : null;
1621    }
1622
1623    public void setXfertime(long xfertime) {
1624        _xfertime = xfertime;
1625    }
1626
1627    public String JavaDoc toString() {
1628        StringBuffer JavaDoc ret = new StringBuffer JavaDoc();
1629        ret.append("LinkedRemoteFile[\"" + this.getName() + "\",");
1630        if (isFile()) {
1631            ret.append("xfertime:" + _xfertime + ",");
1632        }
1633        if (isDeleted())
1634            ret.append("deleted,");
1635        if (isLink())
1636            ret.append("link:" + getLinkPath() + ",");
1637        //ret.append(slaves);
1638
if (_slaves != null) {
1639            Iterator JavaDoc i = _slaves.iterator();
1640            // Enumeration e = slaves.elements();
1641
ret.append("slaves:[");
1642            // HACK: How can one find out the endpoint without using regexps?
1643
//Pattern p = Pattern.compile("endpoint:\\[(.*?):.*?\\]");
1644
while (i.hasNext()) {
1645                RemoteSlave rslave = (RemoteSlave) i.next();
1646                if (rslave == null)
1647                    throw new FatalException("There's a null in rslaves");
1648                ret.append(rslave.getName());
1649                if (!rslave.isAvailable())
1650                    ret.append("-OFFLINE");
1651                if (i.hasNext())
1652                    ret.append(",");
1653            }
1654            ret.append("]");
1655        }
1656        if (isDirectory())
1657            ret.append("[directory(" + _files.size() + ")]");
1658        ret.append("]");
1659        return ret.toString();
1660    }
1661
1662    public void unmergeDir(RemoteSlave rslave) {
1663        if (!isDirectory())
1664            throw new IllegalStateException JavaDoc();
1665
1666        for (Iterator JavaDoc i = getFiles().iterator(); i.hasNext();) {
1667            LinkedRemoteFileInterface file =
1668                (LinkedRemoteFileInterface) i.next();
1669            if (file.isDirectory()) {
1670                file.unmergeDir(rslave);
1671                //remove empty deleted directories
1672
if (file.isDeleted() && file.dirSize() == 0) {
1673                    i.remove();
1674                    // size SHOULD be 0, but if it isn't, this will even out
1675
// the unsynched dirsize
1676
if (file.length() != 0)
1677                        logger.warn(
1678                            "file.length() == "
1679                                + file.length()
1680                                + " for unmerged directory");
1681                    addSize(-file.length());
1682                }
1683            } else {
1684                file.unmergeFile(rslave);
1685            }
1686        }
1687    }
1688
1689    public void unmergeFile(RemoteSlave rslave) {
1690        if (!isFile())
1691            throw new IllegalStateException JavaDoc();
1692
1693        if (removeSlave(rslave)) {
1694            logger.warn(getPath() + " deleted from " + rslave.getName());
1695        }
1696        //it's safe to remove it as it has no slaves.
1697
// removeSlave() takes care of this
1698
// if (file.getSlaves().size() == 0) {
1699
// i.remove();
1700
// getParentFileNull().addSize(-file.length());
1701
// }
1702
}
1703
1704}
1705
Popular Tags