0001: /*
0002: * This file is part of DrFTPD, Distributed FTP Daemon.
0003: *
0004: * DrFTPD is free software; you can redistribute it and/or modify
0005: * it under the terms of the GNU General Public License as published by
0006: * the Free Software Foundation; either version 2 of the License, or
0007: * (at your option) any later version.
0008: *
0009: * DrFTPD is distributed in the hope that it will be useful,
0010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0012: * GNU General Public License for more details.
0013: *
0014: * You should have received a copy of the GNU General Public License
0015: * along with DrFTPD; if not, write to the Free Software
0016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0017: */
0018: package org.drftpd.remotefile;
0019:
0020: import java.io.FileNotFoundException;
0021: import java.io.IOException;
0022: import java.io.Serializable;
0023: import java.util.ArrayList;
0024: import java.util.Collection;
0025: import java.util.Collections;
0026: import java.util.Iterator;
0027: import java.util.List;
0028: import java.util.Map;
0029: import java.util.Stack;
0030: import java.util.StringTokenizer;
0031:
0032: import net.sf.drftpd.FatalException;
0033: import net.sf.drftpd.FileExistsException;
0034: import net.sf.drftpd.NoAvailableSlaveException;
0035: import net.sf.drftpd.ObjectNotFoundException;
0036: import net.sf.drftpd.SlaveUnavailableException;
0037: import net.sf.drftpd.master.config.ConfigInterface;
0038: import net.sf.drftpd.master.config.FtpConfig;
0039:
0040: import org.apache.log4j.Level;
0041: import org.apache.log4j.Logger;
0042: import org.drftpd.SFVFile;
0043: import org.drftpd.id3.ID3Tag;
0044: import org.drftpd.master.ConnectionManager;
0045: import org.drftpd.master.RemoteSlave;
0046: import org.drftpd.master.RemoteTransfer;
0047: import org.drftpd.slave.RemoteIOException;
0048: import org.drftpd.usermanager.User;
0049:
0050: import se.mog.io.File;
0051:
0052: /**
0053: * Represents the file attributes of a remote file.
0054: *
0055: * @author mog
0056: * @version $Id: LinkedRemoteFile.java 1541 2006-12-16 17:42:04Z zubov $
0057: */
0058:
0059: public class LinkedRemoteFile implements Serializable, Comparable,
0060: LinkedRemoteFileInterface {
0061: private static final Logger logger = Logger
0062: .getLogger(LinkedRemoteFile.class.getName());
0063:
0064: static final long serialVersionUID = 3585958839961835107L;
0065:
0066: private long _checkSum;
0067:
0068: // cannot be generic cause CaseInsensitiveHashtable isn't generic
0069: // it's used by slave when sending commands.
0070: private CaseInsensitiveHashtable _files;
0071:
0072: private transient ConfigInterface _ftpConfig;
0073:
0074: private String _group;
0075:
0076: // private Random rand = new Random();
0077: // private String path;
0078: private long _lastModified;
0079:
0080: private long _length;
0081:
0082: private String _link;
0083:
0084: private String _name;
0085:
0086: private String _owner;
0087:
0088: private LinkedRemoteFile _parent;
0089:
0090: // ///////////////////// SLAVES
0091: protected List<RemoteSlave> _slaves;
0092:
0093: private long _xfertime = 0;
0094:
0095: protected SFVFile _sfvFile;
0096:
0097: protected ID3Tag mp3tag;
0098:
0099: /**
0100: * Creates an empty RemoteFile directory, usually used as an empty root
0101: * directory that <link>{merge()} </link> can be called on.
0102: *
0103: * Used if no file database exists to start a tree from scratch.
0104: */
0105: public LinkedRemoteFile(ConfigInterface ftpConfig) {
0106: _ftpConfig = ftpConfig;
0107:
0108: _lastModified = System.currentTimeMillis();
0109: _length = 0;
0110: _parent = null;
0111: _name = "";
0112: _files = new CaseInsensitiveHashtable();
0113: _slaves = null;
0114: }
0115:
0116: /**
0117: * Creates a LinkedRemoteFile from file or creates a directory tree
0118: * representation.
0119: *
0120: * Used by DirectoryRemoteFile. Called by other constructor,
0121: * ConnectionManager is null if called from Slave.
0122: *
0123: * They all end up here.
0124: *
0125: * @param parent
0126: * the parent of this file
0127: * @param file
0128: * file that this RemoteFile object should represent.
0129: */
0130: private LinkedRemoteFile(LinkedRemoteFile parent,
0131: RemoteFileInterface file, ConfigInterface cfg) {
0132: this (parent, file, file.getName(), cfg);
0133: }
0134:
0135: private LinkedRemoteFile(LinkedRemoteFile parent,
0136: RemoteFileInterface file, String name, ConfigInterface cfg) {
0137: if (!ListUtils.isLegalFileName(name)) {
0138: throw new IllegalArgumentException("Illegal filename - "
0139: + parent.getPath() + "/" + name);
0140: }
0141:
0142: if (!file.isFile() && !file.isDirectory()) {
0143: throw new IllegalArgumentException(
0144: "File is not a file nor a directory: " + file);
0145: }
0146:
0147: if (_length == -1) {
0148: throw new IllegalArgumentException("length() == -1 for "
0149: + file);
0150: }
0151:
0152: _ftpConfig = cfg;
0153: _lastModified = file.lastModified();
0154: setOwner(file.getUsername());
0155: setGroup(file.getGroupname());
0156: _checkSum = file.getCheckSumCached();
0157: _parent = parent;
0158:
0159: if (file.isLink()) {
0160: _link = file.getLinkPath();
0161: }
0162:
0163: if (parent == null) {
0164: _name = "";
0165: } else {
0166: _name = name;
0167: }
0168:
0169: if (file.isFile()) {
0170: _length = file.length();
0171: _xfertime = file.getXfertime();
0172: _slaves = Collections
0173: .synchronizedList(new ArrayList<RemoteSlave>(file
0174: .getSlaves()));
0175:
0176: try {
0177: getParentFile().addSize(length());
0178: } catch (FileNotFoundException ok) {
0179: // thrown if this is the root dir
0180: }
0181: } else if (file.isDirectory()) {
0182: // RemoteFileInterface dir[] = file.listFiles();
0183: // if (name != "" && dir.length == 0)
0184: // throw new FatalException(
0185: // "Constructor called with empty dir: " + file);
0186: _files = new CaseInsensitiveHashtable(file.getFiles()
0187: .size());
0188:
0189: Stack<RemoteFileInterface> dirstack = new Stack<RemoteFileInterface>();
0190:
0191: // for (int i = 0; i < dir.length; i++) {
0192: for (Iterator iter = file.getFiles().iterator(); iter
0193: .hasNext();) {
0194: RemoteFileInterface file2 = (RemoteFileInterface) iter
0195: .next();
0196:
0197: // RemoteFileInterface file2 = dir[i];
0198: if (file2.isDirectory()) {
0199: dirstack.push(file2);
0200:
0201: continue;
0202: }
0203:
0204: // the constructor takes care of addSize()
0205: _files.put(file2.getName(), new LinkedRemoteFile(this ,
0206: file2, _ftpConfig));
0207: }
0208:
0209: Iterator i = dirstack.iterator();
0210:
0211: while (i.hasNext()) {
0212: RemoteFileInterface file2 = (RemoteFileInterface) i
0213: .next();
0214: String filename = file2.getName();
0215:
0216: // the constructor takes care of addSize()
0217: _files.put(filename, new LinkedRemoteFile(this , file2,
0218: _ftpConfig));
0219: }
0220: } else {
0221: throw new RuntimeException();
0222: }
0223:
0224: // parent == null if creating root dir
0225: }
0226:
0227: /**
0228: * Creates a root directory (parent == null) that FileRemoteFile or
0229: * JDOMRemoteFile is merged on.
0230: *
0231: * Also called with null ConnectionManager from slave
0232: */
0233: public LinkedRemoteFile(RemoteFileInterface file, FtpConfig cfg)
0234: throws IOException {
0235: this (null, file, cfg);
0236: }
0237:
0238: /**
0239: * Updates lastModified() on this directory, use putFile() to avoid it.
0240: */
0241: public LinkedRemoteFile addFile(RemoteFileInterface file) {
0242: _lastModified = System.currentTimeMillis();
0243:
0244: return putFile(file);
0245: }
0246:
0247: protected void addSize(long size) {
0248: if (isDirectory()) {
0249: synchronized (_files) {
0250: _length += size;
0251: }
0252: } else {
0253: _length += size;
0254: }
0255:
0256: // logger.debug(
0257: // this +" got " + size + " added, now " + _length,
0258: // new Throwable());
0259: try {
0260: getParentFile().addSize(size);
0261: } catch (FileNotFoundException done) {
0262: }
0263: }
0264:
0265: public void addSlave(RemoteSlave slave) {
0266: if (isDirectory()) { // isDirectory()
0267: throw new IllegalStateException(
0268: "Cannot addSlave() on a directory");
0269: }
0270:
0271: if (slave == null) {
0272: throw new NullPointerException();
0273: }
0274:
0275: // we get lots of duplicate adds when merging and the slave is already
0276: // in the file database
0277: if (_slaves.contains(slave)) {
0278: return;
0279: }
0280:
0281: _slaves.add(slave);
0282: }
0283:
0284: /**
0285: * @throws ClassCastException
0286: * if object is not an instance of RemoteFileInterface.
0287: * @see java.lang.Comparable#compareTo(java.lang.Object)
0288: */
0289: public int compareTo(Object o) {
0290: return getName().compareTo(((RemoteFileInterface) o).getName());
0291: }
0292:
0293: /**
0294: * Follows links
0295: */
0296: public LinkedRemoteFile createDirectories(String path) {
0297: NonExistingFile nef = lookupNonExistingFile(path);
0298:
0299: if (nef.exists()) {
0300: try {
0301: LinkedRemoteFile temp = lookupFile(path);
0302: if (temp.isDirectory()) {
0303: return temp;
0304: }
0305: throw new RuntimeException(
0306: "Destination directory is already a file");
0307: } catch (FileNotFoundException e) {
0308: throw new RuntimeException(
0309: "createDirectories called on already existing directory that cannot be found");
0310: }
0311: }
0312:
0313: LinkedRemoteFile dir = nef.getFile();
0314: StringTokenizer st = new StringTokenizer(nef.getPath(), "/");
0315:
0316: while (st.hasMoreTokens()) {
0317: try {
0318: dir = dir.createDirectory(st.nextToken());
0319: } catch (FileExistsException e) {
0320: throw new RuntimeException(e);
0321: }
0322: }
0323:
0324: return dir;
0325: }
0326:
0327: public LinkedRemoteFile createDirectory(String fileName)
0328: throws FileExistsException {
0329: return createDirectory(null, null, fileName);
0330: }
0331:
0332: public LinkedRemoteFile createDirectory(String owner, String group,
0333: String fileName) throws FileExistsException {
0334: // LinkedRemoteFile existingfile = (LinkedRemoteFile)
0335: // _files.get(fileName);
0336: // //throws NullPointerException on non-existing directories
0337: // if (existingfile.isDeleted())
0338: // existingfile.delete();
0339: // existingfile = (LinkedRemoteFile) _files.get(fileName);
0340: if (hasFile(fileName)) {
0341: throw new FileExistsException(fileName
0342: + " already exists in this directory");
0343: }
0344:
0345: LinkedRemoteFile file = addFile(new StaticRemoteFile(null,
0346: fileName, owner, group, 0L, System.currentTimeMillis()));
0347: logger.info("Created directory " + file);
0348: _lastModified = System.currentTimeMillis();
0349:
0350: return file;
0351: }
0352:
0353: private void setDeletedRecursive() {
0354: if (isFile()) {
0355: _parent = null;
0356: _slaves.clear();
0357: } else if (isDirectory()) {
0358: for (RemoteFileInterface victim : new ArrayList<RemoteFileInterface>(
0359: getFiles())) {
0360: if (victim instanceof LinkedRemoteFile) {
0361: LinkedRemoteFile lrf = (LinkedRemoteFile) victim;
0362: lrf.setDeletedRecursive();
0363: }
0364: }
0365: _parent = null;
0366: _files.clear();
0367: }
0368: }
0369:
0370: /**
0371: * Deletes a file or directory, RemoteSlave handles issues with slaves being
0372: * offline and queued deletes
0373: *
0374: * Trying to lookupFile() or getFile() a deleted file throws
0375: * FileNotFoundException.
0376: */
0377: public void delete() {
0378: logger.debug("delete(" + getPath() + ")");
0379:
0380: if (isDirectory()) {
0381: // need to use a copy of getFiles() for recursive delete to avoid
0382: // ConcurrentModificationErrors
0383: long tempLength = length();
0384: _length = 0;
0385: _ftpConfig.getGlobalContext().getSlaveManager()
0386: .deleteOnAllSlaves(this );
0387: try {
0388: getParentFile().getMap().remove(getName());
0389:
0390: // now need to remove size of directory from it's parent
0391: getParentFile().addSize(-tempLength);
0392:
0393: } catch (FileNotFoundException ex) {
0394: logger.log(Level.FATAL,
0395: "FileNotFoundException on getParentFile()", ex);
0396: }
0397: setDeletedRecursive();
0398: return;
0399: }
0400: if (_link == null) {
0401: // this.isFile() = true
0402: for (RemoteSlave rslave : new ArrayList<RemoteSlave>(
0403: _slaves)) {
0404: try {
0405: for (RemoteTransfer rtransfer : new ArrayList<RemoteTransfer>(
0406: rslave.getTransfers())) {
0407: if (getPath().equalsIgnoreCase(
0408: rtransfer.getPathNull())) {
0409: rtransfer
0410: .abort("File has been deleted on the master");
0411: }
0412: }
0413: } catch (SlaveUnavailableException e) {
0414: // nothing to do here, still want to delete it though
0415: }
0416: }
0417: _ftpConfig.getGlobalContext().getSlaveManager()
0418: .deleteOnAllSlaves(this );
0419: _slaves.clear();
0420: } else {
0421: _link = null;
0422: }
0423:
0424: try {
0425: if (getParentFile().getMap().remove(getName()) == null) {
0426: logger.debug("Unable to remove " + getPath()
0427: + " from parent dir, already deleted?");
0428: }
0429:
0430: getParentFileNull().addSize(-length());
0431: } catch (FileNotFoundException ex) {
0432: logger.log(Level.FATAL,
0433: "FileNotFoundException on getParentFile()", ex);
0434: }
0435: }
0436:
0437: public void deleteFromSlave(RemoteSlave rslave) {
0438: if (!_slaves.contains((rslave)))
0439: throw new IllegalArgumentException(
0440: "Cannot delete file from " + rslave.getName()
0441: + " as it's not on it");
0442: rslave.simpleDelete(getPath());
0443: removeSlave(rslave);
0444: }
0445:
0446: public long dirSize() {
0447: if (_files == null) {
0448: throw new IllegalStateException(
0449: "Cannot be called on a non-directory");
0450: }
0451:
0452: return _files.size();
0453: }
0454:
0455: public boolean equals(Object obj) {
0456: if (obj instanceof LinkedRemoteFileInterface
0457: && ((LinkedRemoteFileInterface) obj).getPath().equals(
0458: getPath())) {
0459: return true;
0460: }
0461:
0462: return false;
0463: }
0464:
0465: public Collection<RemoteSlave> getAvailableSlaves()
0466: throws NoAvailableSlaveException {
0467: ArrayList<RemoteSlave> availableSlaves = new ArrayList<RemoteSlave>();
0468:
0469: for (RemoteSlave rslave : getSlaves()) {
0470:
0471: if (!rslave.isAvailable()) {
0472: continue;
0473: }
0474:
0475: availableSlaves.add(rslave);
0476: }
0477:
0478: if (availableSlaves.isEmpty()) {
0479: throw new NoAvailableSlaveException(getPath()
0480: + " has 0 slaves online");
0481: }
0482:
0483: return availableSlaves;
0484: }
0485:
0486: /**
0487: * Uses cached checksum if the cached checksum is not 0
0488: */
0489: public long getCheckSum() throws NoAvailableSlaveException {
0490: if ((_checkSum == 0) && (_length != 0)) {
0491: _checkSum = getCheckSumFromSlave();
0492:
0493: if (_checkSum == 0) {
0494: throw new NoAvailableSlaveException(
0495: "Could not find a slave to check crc of "
0496: + getPath());
0497: }
0498: }
0499:
0500: return _checkSum;
0501: }
0502:
0503: /**
0504: * Returns the cached checksum or 0 if no checksum was cached.
0505: * <p>
0506: * Use {getCheckSum()} to automatically calculate checksum if no cached
0507: * checksum is available.
0508: */
0509: public long getCheckSumCached() {
0510: return _checkSum;
0511: }
0512:
0513: /**
0514: * Returns 0 if the checksum cannot be read.
0515: */
0516: public long getCheckSumFromSlave() {
0517: try {
0518: for (Iterator iter = getAvailableSlaves().iterator(); iter
0519: .hasNext();) {
0520: RemoteSlave slave = (RemoteSlave) iter.next();
0521: String index = null;
0522:
0523: try {
0524: index = slave.issueChecksumToSlave(getPath());
0525: _checkSum = slave.fetchChecksumFromIndex(index);
0526: } catch (RemoteIOException e2) {
0527: continue;
0528: } catch (SlaveUnavailableException e2) {
0529: continue;
0530: }
0531:
0532: return _checkSum;
0533: }
0534: } catch (NoAvailableSlaveException e) {
0535: return 0;
0536: }
0537:
0538: return 0;
0539: }
0540:
0541: public Collection<LinkedRemoteFileInterface> getDirectories() {
0542: Collection temp = getFiles();
0543: ArrayList<LinkedRemoteFileInterface> retlist = new ArrayList<LinkedRemoteFileInterface>();
0544:
0545: for (Iterator iter = temp.iterator(); iter.hasNext();) {
0546: LinkedRemoteFileInterface file = (LinkedRemoteFileInterface) iter
0547: .next();
0548:
0549: if (file.isDirectory()) {
0550: retlist.add(file);
0551: }
0552: }
0553:
0554: return retlist;
0555: }
0556:
0557: /**
0558: * Returns fileName contained in this directory.
0559: *
0560: * @param fileName
0561: * @throws FileNotFoundException
0562: * if fileName doesn't exist in the files Map
0563: */
0564: public LinkedRemoteFileInterface getFile(String fileName)
0565: throws FileNotFoundException {
0566: LinkedRemoteFileInterface file = (LinkedRemoteFileInterface) _files
0567: .get(fileName);
0568:
0569: if (file == null) {
0570: throw new FileNotFoundException(
0571: "No such file or directory: " + fileName);
0572: }
0573:
0574: return file;
0575: }
0576:
0577: /**
0578: * Returns a Collection of all the LinkedRemoteFile objects in this
0579: * directory, with all .isDeleted() files removed.
0580: *
0581: * <p>
0582: * <b>NOTE: </b>Since DrFTPD 1.2, the collection returned can no longer be
0583: * directly modified.
0584: *
0585: * <p>
0586: * Since this method overrides FileRemoteFile which is part of the classes
0587: * that keep 1.4 compatibility for the slaves it cannot use generics.
0588: * </p>
0589: *
0590: * @return a Collection of all the LinkedRemoteFile objects in this
0591: * directory.
0592: */
0593: public Collection<LinkedRemoteFileInterface> getFiles2() {
0594: if (_files == null) {
0595: throw new IllegalStateException("Isn't a directory");
0596: }
0597: return getFilesMap().values();
0598: }
0599:
0600: public Collection<RemoteFileInterface> getFiles() {
0601: if (_files == null) {
0602: throw new IllegalStateException("Isn't a directory");
0603: }
0604: return getFilesMap().values();
0605: }
0606:
0607: /**
0608: * Returns a map for this directory, having String name as key and
0609: * LinkedRemoteFile file as value, with all .isDeleted() files removed.
0610: *
0611: * <p>
0612: * <b>NOTE: </b>Since DrFTPD 1.2, the map returned can no longer be directly
0613: * modified.
0614: *
0615: * @return map for this directory, having String name as key and
0616: * LinkedRemoteFile file as value, with all .isDeleted() files
0617: * removed.
0618: */
0619: private Map getFilesMap() {
0620: return Collections.unmodifiableMap(_files);
0621: }
0622:
0623: public String getGroupname() {
0624: if ((_group == null) || _group.equals("")) {
0625: return "drftpd";
0626: }
0627:
0628: return _group;
0629: }
0630:
0631: public LinkedRemoteFile getLink() throws FileNotFoundException {
0632: return getParentFile().lookupFile(getLinkPath());
0633: }
0634:
0635: public String getLinkPath() {
0636: if (_link == null) {
0637: throw new NullPointerException();
0638: }
0639:
0640: return _link;
0641: }
0642:
0643: /**
0644: * Returns the underlying Map for this directory.
0645: *
0646: * It is dangerous to modify without knowing what you're doing. Dirsize
0647: * needs to be taken into account as well as sending approperiate commands
0648: * to the slaves.
0649: *
0650: * @return the underlying Map for this directory.
0651: */
0652: public Map getMap() {
0653: return _files;
0654: }
0655:
0656: public String getName() {
0657: return _name;
0658: }
0659:
0660: public String getParent() throws FileNotFoundException {
0661: return getParentFile().getPath();
0662: }
0663:
0664: public LinkedRemoteFile getParentFile()
0665: throws FileNotFoundException {
0666: if (_parent == null) {
0667: throw new FileNotFoundException(
0668: "root directory has no parent");
0669: }
0670:
0671: return _parent;
0672: }
0673:
0674: public LinkedRemoteFile getParentFileNull() {
0675: return _parent;
0676: }
0677:
0678: public String getPath() {
0679: StringBuffer path = new StringBuffer();
0680: LinkedRemoteFileInterface parent = this ;
0681:
0682: while (true) {
0683: if (parent.getName().length() == 0) {
0684: break;
0685: }
0686:
0687: path.insert(0, "/" + parent.getName());
0688:
0689: try {
0690: parent = parent.getParentFile();
0691:
0692: // throws FileNotFoundException
0693: } catch (FileNotFoundException ex) {
0694: break;
0695: }
0696: }
0697:
0698: if (path.length() == 0) {
0699: return "/";
0700: }
0701:
0702: return path.toString();
0703: }
0704:
0705: public LinkedRemoteFile getRoot() {
0706: LinkedRemoteFile root = this ;
0707:
0708: try {
0709: while (true)
0710: root = root.getParentFile();
0711: } catch (FileNotFoundException ex) {
0712: return root;
0713: }
0714: }
0715:
0716: public synchronized SFVFile getSFVFile() throws IOException,
0717: FileNotFoundException, NoAvailableSlaveException,
0718: FileStillTransferringException {
0719:
0720: if (_xfertime == -1L) {
0721: throw new FileStillTransferringException();
0722: }
0723:
0724: if (_sfvFile == null) {
0725: Collection<RemoteSlave> availSlaves = new ArrayList<RemoteSlave>(
0726: getAvailableSlaves());
0727: for (RemoteSlave rslave : availSlaves) {
0728: try {
0729: String index = rslave
0730: .issueSFVFileToSlave(getPath());
0731: _sfvFile = new SFVFile(rslave
0732: .fetchSFVFileFromIndex(index));
0733: _sfvFile.setCompanion(this );
0734:
0735: return _sfvFile;
0736: } catch (RemoteIOException e) {
0737: if (e.getCause() instanceof FileNotFoundException) {
0738: removeSlave(rslave);
0739: throw (FileNotFoundException) e.getCause();
0740: }
0741:
0742: throw (IOException) e.getCause();
0743: } catch (SlaveUnavailableException e) {
0744: continue;
0745: }
0746: }
0747: throw new NoAvailableSlaveException();
0748: }
0749:
0750: if (_sfvFile.size() == 0) {
0751: //TODO remove invalidation step, use file locking.
0752: _sfvFile = null; // no need to keep a worthless sfv file
0753: throw new FileNotFoundException(
0754: "sfv file contains no checksum entries");
0755: }
0756:
0757: return _sfvFile;
0758: }
0759:
0760: public synchronized ID3Tag getID3v1Tag()
0761: throws NoAvailableSlaveException, FileNotFoundException,
0762: IOException {
0763: if (mp3tag == null) {
0764: logger.info("getID3v1Tag() : (file) " + getPath());
0765:
0766: while (true) {
0767: RemoteSlave rslave = null;
0768: if (isAvailable()) {
0769: Iterator<RemoteSlave> iter = getAvailableSlaves()
0770: .iterator();
0771: if (!iter.hasNext()) {
0772: throw new NoAvailableSlaveException();
0773: }
0774: rslave = getAvailableSlaves().iterator().next();
0775: }
0776:
0777: if (rslave == null) {
0778: throw new NoAvailableSlaveException(
0779: "no available slave for ID3Tag - "
0780: + getPath());
0781: }
0782:
0783: try {
0784: String index = rslave.issueID3TagToSlave(getPath());
0785: mp3tag = rslave.fetchID3TagFromIndex(index);
0786:
0787: break;
0788: } catch (SlaveUnavailableException e) {
0789: continue;
0790: } catch (RemoteIOException e) {
0791: if (e.getCause() instanceof FileNotFoundException) {
0792: removeSlave(rslave);
0793: throw (FileNotFoundException) e.getCause();
0794: }
0795:
0796: throw (IOException) e.getCause();
0797: }
0798: }
0799: } else {
0800: logger.info("getID3v1Tag() : (cached) " + getPath());
0801: }
0802:
0803: if (mp3tag == null) {
0804: throw new IOException("No id3tag found for " + getPath());
0805: }
0806:
0807: return mp3tag;
0808: }
0809:
0810: /**
0811: * returns slaves. throws exception if a directory.
0812: */
0813: public List<RemoteSlave> getSlaves() {
0814: if (isDirectory()) {
0815: throw new IllegalStateException(
0816: "getSlaves() called on a directory: " + getPath());
0817: }
0818: return _slaves;
0819: }
0820:
0821: public String getUsername() {
0822: if ((_owner == null) || _owner.equals("")) {
0823: return "nobody";
0824: }
0825:
0826: return _owner;
0827: }
0828:
0829: public long getXferspeed() {
0830: if (getXfertime() <= 0) {
0831: return 0;
0832: }
0833:
0834: return (length() / getXfertime());
0835: }
0836:
0837: /**
0838: * @return xfertime in milliseconds
0839: */
0840: public long getXfertime() {
0841: return _xfertime;
0842: }
0843:
0844: /**
0845: * Returns true if this directory contains a file named filename, this is
0846: * case sensitive.
0847: *
0848: * @param filename
0849: * The name of the file
0850: * @return true if this directory contains a file named filename, this is
0851: * case sensitive.
0852: */
0853: public boolean hasFile(String filename) {
0854: return _files.containsKey(filename);
0855: }
0856:
0857: public int hashCode() {
0858: return getName().hashCode();
0859: }
0860:
0861: /**
0862: * Returns true if this file or directory uses slaves that are currently
0863: * offline.
0864: *
0865: * @return true if this file or directory uses slaves that are currently
0866: * offline.
0867: */
0868: public boolean hasOfflineSlaves() {
0869: if (isFile()) {
0870: for (Iterator iter = getSlaves().iterator(); iter.hasNext();) {
0871: RemoteSlave rslave = (RemoteSlave) iter.next();
0872:
0873: if (rslave == null) {
0874: throw new NullPointerException();
0875: }
0876:
0877: if (!rslave.isAvailable()) {
0878: return true;
0879: }
0880: }
0881: } else if (isDirectory()) {
0882: for (Iterator iter = getFiles().iterator(); iter.hasNext();) {
0883: if (((LinkedRemoteFileInterface) iter.next())
0884: .hasOfflineSlaves()) {
0885: return true;
0886: }
0887: }
0888: }
0889:
0890: return false;
0891: }
0892:
0893: public boolean hasSlave(RemoteSlave slave) {
0894: return _slaves.contains(slave);
0895: }
0896:
0897: /**
0898: * Does file have online slaves?
0899: *
0900: * @return Always true for directories
0901: */
0902: public boolean isAvailable() {
0903: if (isDirectory()) {
0904: return true;
0905: }
0906: if (isLink()) {
0907: return true;
0908: }
0909:
0910: for (Iterator iter = getSlaves().iterator(); iter.hasNext();) {
0911: RemoteSlave rslave = (RemoteSlave) iter.next();
0912:
0913: if (rslave == null) {
0914: throw new RuntimeException();
0915: }
0916:
0917: if (rslave.isAvailable()) {
0918: return true;
0919: }
0920: }
0921:
0922: return false;
0923: }
0924:
0925: public boolean isDirectory() {
0926: return (_files != null) && !isLink();
0927: }
0928:
0929: /**
0930: * @return true if directory is empty.
0931: */
0932: private boolean isEmpty() {
0933: if (_files == null) {
0934: throw new IllegalStateException("_files is null");
0935: }
0936:
0937: return _files.isEmpty();
0938: }
0939:
0940: public boolean isFile() {
0941: return (_files == null) && (_slaves != null) && !isLink();
0942: }
0943:
0944: public boolean isLink() {
0945: return _link != null;
0946: }
0947:
0948: public boolean isValid() {
0949: if (_parent == null)
0950: return true;
0951: try {
0952: if (_parent.getFile(getName()) == this && _parent.isValid())
0953: return true;
0954: return false;
0955: } catch (FileNotFoundException e) {
0956: return false;
0957: }
0958: }
0959:
0960: public long lastModified() {
0961: return _lastModified;
0962: }
0963:
0964: public long length() {
0965: // if (isDirectory()) {
0966: // long length = 0;
0967: // for (Iterator iter = getFiles().iterator(); iter.hasNext();) {
0968: // length += ((LinkedRemoteFile) iter.next()).length();
0969: // }
0970: // if (_length != 0 && length != _length)
0971: // logger.warn(
0972: // "",
0973: // new Throwable(
0974: // "Cached checksum missmatch: "
0975: // + length
0976: // + " != "
0977: // + _length
0978: // + " for "
0979: // + toString()));
0980: // _length = length;
0981: // return length;
0982: // }
0983: if (_length < 0) {
0984: return 0;
0985: }
0986:
0987: return _length;
0988: }
0989:
0990: public LinkedRemoteFile lookupFile(String path)
0991: throws FileNotFoundException {
0992: return lookupFile(path, true);
0993: }
0994:
0995: public LinkedRemoteFile lookupFile(String path, boolean followLinks)
0996: throws FileNotFoundException {
0997: NonExistingFile ret = lookupNonExistingFile(path, followLinks);
0998:
0999: if (!ret.exists()) {
1000: throw new FileNotFoundException(path + ": File not found");
1001: }
1002:
1003: return ret.getFile();
1004: }
1005:
1006: public NonExistingFile lookupNonExistingFile(String path) {
1007: return lookupNonExistingFile(path, true);
1008: }
1009:
1010: public NonExistingFile lookupNonExistingFile(String path,
1011: boolean followLinks) {
1012: if (path == null) {
1013: throw new IllegalArgumentException("null path not allowed");
1014: }
1015:
1016: if (path.equals("")) {
1017: return new NonExistingFile(this , null);
1018: }
1019:
1020: LinkedRemoteFile currFile = this ;
1021:
1022: if (path.charAt(0) == '/') {
1023: currFile = getRoot();
1024: }
1025:
1026: // check for leading ~
1027: if ((path.length() == 1) && path.equals("~")) {
1028: currFile = getRoot();
1029: path = "";
1030: } else if ((path.length() >= 2)
1031: && path.substring(0, 2).equals("~/")) {
1032: currFile = getRoot();
1033: path = path.substring(2);
1034: }
1035:
1036: StringTokenizer st = new StringTokenizer(path, "/");
1037:
1038: while (st.hasMoreTokens()) {
1039: String currFileName = st.nextToken();
1040:
1041: if (currFileName.equals(".")) {
1042: continue;
1043: }
1044:
1045: if (currFileName.equals("..")) {
1046: try {
1047: currFile = currFile.getParentFile();
1048: } catch (FileNotFoundException ex) {
1049: }
1050:
1051: continue;
1052: }
1053:
1054: LinkedRemoteFile nextFile;
1055:
1056: try {
1057: nextFile = (LinkedRemoteFile) currFile
1058: .getFile(currFileName);
1059:
1060: if (nextFile.isLink() && followLinks) {
1061: currFile = currFile.lookupFile(nextFile
1062: .getLinkPath());
1063: } else {
1064: currFile = nextFile;
1065: }
1066: } catch (FileNotFoundException ex) {
1067: StringBuffer remaining = new StringBuffer(currFileName);
1068:
1069: if (st.hasMoreElements()) {
1070: remaining.append('/').append(st.nextToken(""));
1071: }
1072:
1073: return new NonExistingFile(currFile, remaining
1074: .toString());
1075: }
1076: }
1077:
1078: return new NonExistingFile(currFile, null);
1079: }
1080:
1081: /**
1082: * Returns path for a non-existing file. Performs path normalization and
1083: * returns an absolute path, follows links
1084: */
1085: public String lookupPath(String path) {
1086: NonExistingFile ret = lookupNonExistingFile(path);
1087:
1088: if (ret.exists()) {
1089: return ret.getFile().getPath();
1090: }
1091:
1092: return ret.getFile().getPath() + '/' + ret.getPath();
1093: }
1094:
1095: public SFVFile lookupSFVFile() throws IOException,
1096: FileNotFoundException, NoAvailableSlaveException,
1097: FileStillTransferringException {
1098: if (!isDirectory()) {
1099: throw new IllegalStateException(
1100: "lookupSFVFile must be called on a directory");
1101: }
1102:
1103: for (Iterator iter = getFiles().iterator(); iter.hasNext();) {
1104: LinkedRemoteFileInterface myFile = (LinkedRemoteFileInterface) iter
1105: .next();
1106:
1107: if (myFile.getName().toLowerCase().endsWith(".sfv")
1108: && myFile.isFile()) {
1109: return myFile.getSFVFile();
1110: }
1111: }
1112:
1113: throw new FileNotFoundException("no sfv file in directory");
1114: }
1115:
1116: public String lookupMP3File() throws IOException,
1117: FileNotFoundException, NoAvailableSlaveException {
1118: if (!isDirectory()) {
1119: throw new IllegalStateException(
1120: "lookupMP3File() must be called on a directory");
1121: }
1122:
1123: for (Iterator iter = getFiles().iterator(); iter.hasNext();) {
1124: LinkedRemoteFileInterface myFile = (LinkedRemoteFileInterface) iter
1125: .next();
1126:
1127: if (myFile.getName().toLowerCase().endsWith(".mp3")
1128: && myFile.isFile()) {
1129: return myFile.getPath();
1130: }
1131: }
1132:
1133: throw new FileNotFoundException("no mp3 file in directory");
1134: }
1135:
1136: /**
1137: * Use addFile() if you want lastModified to be updated.
1138: */
1139: public LinkedRemoteFile putFile(RemoteFileInterface file) {
1140: return putFile(file, file.getName());
1141: }
1142:
1143: /**
1144: * @param torslave
1145: * RemoteSlave to replicate to.
1146: */
1147:
1148: // public void replicate(final RemoteSlave torslave)
1149: // throws NoAvailableSlaveException, IOException {
1150: //
1151: // final RemoteSlave fromslave = getASlaveForDownload();
1152: // Transfer fromtransfer = fromslave.getSlave().listen(false);
1153: // final Transfer totransfer =
1154: // torslave.getSlave().connect(
1155: // new InetSocketAddress(
1156: // fromslave.getInetAddress(),
1157: // fromtransfer.getLocalPort()),
1158: // false);
1159: //
1160: // Thread t = new Thread(new Runnable() {
1161: // public void run() {
1162: // try {
1163: // totransfer.receiveFile(
1164: // getParentFile().getPath(),
1165: // 'I',
1166: // getName(),
1167: // 0L);
1168: // } catch (RemoteException e) {
1169: // torslave.handleRemoteException(e);
1170: // logger.warn(EMPTY_STRING, e);
1171: // } catch (FileNotFoundException e) {
1172: // throw new FatalException(e);
1173: // } catch (IOException e) {
1174: // throw new RuntimeException(e);
1175: // }
1176: // }
1177: // });
1178: // t.start();
1179: // fromtransfer.sendFile(getPath(), 'I', 0, false);
1180: // }
1181: /**
1182: * @param toName
1183: * name argument to LinkedRemoteFile constructor
1184: */
1185: private LinkedRemoteFile putFile(RemoteFileInterface file,
1186: String toName) {
1187: if (_files.containsKey(toName)) {
1188: throw new IllegalStateException(getPath() + "/" + toName
1189: + " already exists.");
1190: }
1191:
1192: // validate
1193: if (file.isFile()) {
1194: if (file.getSlaves() == null) {
1195: throw new RuntimeException(file.toString());
1196: }
1197:
1198: for (Iterator iter = file.getSlaves().iterator(); iter
1199: .hasNext();) {
1200: if (iter.next() == null) {
1201: throw new RuntimeException();
1202: }
1203: }
1204: }
1205:
1206: // the constructor takes care of addSize()
1207: LinkedRemoteFile linkedfile = new LinkedRemoteFile(this , file,
1208: toName, _ftpConfig);
1209: _files.put(linkedfile.getName(), linkedfile);
1210:
1211: return linkedfile;
1212: }
1213:
1214: /* */
1215:
1216: /**
1217: * Merges mergedir directory onto <code>this</code> directories. If
1218: * duplicates exist, the slaves are added to this object and the
1219: * file-attributes of the oldest file (lastModified) are kept.
1220: */
1221:
1222: /*
1223: * public void remerge(LinkedRemoteFile mergedir, RemoteSlave rslave) throws
1224: * IOException { if (rslave == null) { throw new
1225: * IllegalArgumentException("slave cannot be null"); }
1226: *
1227: * if (mergedir == null) { throw new IllegalArgumentException("mergedir
1228: * cannot be null"); }
1229: *
1230: * if (!isDirectory()) { throw new IllegalArgumentException( "merge() called
1231: * on a non-directory"); }
1232: *
1233: * if (!mergedir.isDirectory()) { throw new
1234: * IllegalArgumentException("argument is not a directory"); }
1235: *
1236: * synchronized (_files) { // remove all slaves not in mergedir.getFiles() //
1237: * unmerge() gets called on all files not on slave & all directories for
1238: * (Iterator i = Collections.unmodifiableCollection( new
1239: * ArrayList(_files.values())).iterator(); i.hasNext();) { LinkedRemoteFile
1240: * file = (LinkedRemoteFile) i.next();
1241: *
1242: * synchronized (file) { if (mergedir.hasFile(file.getName())) {
1243: * LinkedRemoteFile mergefile = (LinkedRemoteFile)
1244: * mergedir.getFile(file.getName());
1245: *
1246: * if (file.isFile()) { if (file.length() == mergefile.length()) {
1247: * file.addSlave(rslave); } else { //// conflict //// if (mergefile.length() ==
1248: * 0) { logger.log(Level.INFO, "Deleting conflicting 0byte " + mergefile + "
1249: * on " + rslave); rslave.simpleDelete(mergefile.getPath());
1250: * file.unmergeFile(rslave); } else if ((file.getSlaves().size() == 1) &&
1251: * (file.length() == 0)) { logger.info("Deleting conflicting 0byte " + file + "
1252: * on " + rslave);
1253: *
1254: * RemoteSlave rslave2 = (RemoteSlave) file.getSlaves() .iterator() .next();
1255: * rslave2.simpleDelete(file.getPath()); } else { if
1256: * (((file.getSlaves().size() == 1) && file.hasSlave(rslave))) { //we're the
1257: * only slave with the file. file.setLength(mergefile.length());
1258: * file.setCheckSum(0L); file.setLastModified(mergefile.lastModified()); }
1259: * else { //// conflict, rename file... //// rslave.simpleRename(getPath() +
1260: * "/" + mergefile.getName(), getPath(), mergefile.getName() + "." +
1261: * rslave.getName() + ".conflict"); mergefile._name = mergefile._name + "." +
1262: * rslave.getName() + ".conflict"; mergefile.addSlave(rslave);
1263: * _files.put(mergefile.getName(), mergefile); logger.log(Level.WARN, "2 or
1264: * more slaves contained same file with different sizes, renamed to " +
1265: * mergefile.getName()); file.unmergeFile(rslave); } } } } else {
1266: * file.remerge(mergefile, rslave); } // this is done so later new files can
1267: * be merged without overhead of looking through already merged files
1268: * mergedir.getMap().remove(mergefile.getName()); } else { // doesn't have
1269: * file
1270: *
1271: * if (file.isDirectory()) { file.unmergeDir(rslave); } else {
1272: * file.unmergeFile(rslave); } } } }
1273: *
1274: * for (Iterator iter = mergedir.getFiles().iterator(); iter.hasNext();) {
1275: * LinkedRemoteFile newfile = (LinkedRemoteFile) iter.next();
1276: *
1277: * try { getFile(newfile.getName());
1278: *
1279: * continue; // local file exists } catch (FileNotFoundException good) { }
1280: *
1281: * recursiveSetRSlaveAndConfig(newfile, _ftpConfig, rslave); newfile._parent =
1282: * this; // already in _files? _files.put(newfile.getName(), newfile);
1283: * logger.info(newfile.getPath() + " added from " + rslave.getName()); } } }
1284: */
1285:
1286: /**
1287: * Shorthand for _slaves.remove() that checks if _slaves is empty and calls
1288: * .delete() if it is.
1289: */
1290: public boolean removeSlave(RemoteSlave slave) {
1291: if (isDirectory()) {
1292: throw new IllegalStateException(
1293: "Cannot removeSlave() on directory");
1294: }
1295:
1296: boolean ret = _slaves.remove(slave);
1297:
1298: if (_slaves.isEmpty()) {
1299: delete();
1300: }
1301:
1302: return ret;
1303: }
1304:
1305: /**
1306: * Renames this file
1307: */
1308: public void renameTo(String toDirPath, String toName)
1309: throws IOException, FileNotFoundException {
1310: if (toDirPath.charAt(0) != '/') {
1311: throw new RuntimeException(
1312: "renameTo() must be given an absolute path as argument");
1313: }
1314:
1315: if (toName.indexOf('/') != -1) {
1316: throw new RuntimeException(
1317: "Cannot rename to non-existing directory");
1318: }
1319:
1320: if (_ftpConfig == null) {
1321: throw new RuntimeException("_ftpConfig is null: " + this );
1322: }
1323:
1324: LinkedRemoteFile toDir = lookupFile(toDirPath);
1325: try {
1326: if (toDir.getFile(toName) != null) {
1327: throw new FileExistsException(toDirPath
1328: + File.separatorChar + toName
1329: + " already exists");
1330: }
1331: } catch (FileNotFoundException e) {
1332: // this is good
1333: }
1334: // throws FileNotFoundException
1335: {
1336: LinkedRemoteFile tmpDir = toDir;
1337:
1338: do {
1339: if (tmpDir == this ) {
1340: throw new IOException(
1341: "Cannot rename into a subdirectory of self");
1342: }
1343: } while ((tmpDir = tmpDir.getParentFileNull()) != null);
1344: }
1345:
1346: /* existed for old queued operations
1347: * // slaves are copied here too...
1348: LinkedRemoteFile toFile = toDir.putFile(this, toName);
1349: */
1350: _parent._files.remove(getName());
1351: _parent.addSize(-length());
1352: if (isDirectory()) {
1353: _ftpConfig.getGlobalContext().getSlaveManager()
1354: .renameOnAllSlaves(getPath(), toDirPath, toName);
1355: } else { // isFile()
1356: for (Iterator iter = new ArrayList<RemoteSlave>(getSlaves())
1357: .iterator(); iter.hasNext();) {
1358: RemoteSlave rslave = (RemoteSlave) iter.next();
1359: rslave.simpleRename(getPath(), toDirPath, toName);
1360: }
1361: }
1362: _name = toName;
1363: _parent = toDir;
1364: toDir._files.put(getName(), this );
1365: toDir.addSize(length());
1366: }
1367:
1368: public void setCheckSum(long checkSum) {
1369: _checkSum = checkSum;
1370: }
1371:
1372: public void setGroup(String group) {
1373: _group = (group != null) ? group.intern() : null;
1374: }
1375:
1376: public void setLastModified(long lastModified) {
1377: _lastModified = lastModified;
1378: }
1379:
1380: public void setLength(long length) {
1381: getParentFileNull().addSize(length - _length);
1382: _length = length;
1383: }
1384:
1385: public void setOwner(String owner) {
1386: _owner = (owner != null) ? owner.intern() : null;
1387: }
1388:
1389: public void setXfertime(long xfertime) {
1390: _xfertime = xfertime;
1391: }
1392:
1393: public String toString() {
1394: StringBuffer ret = new StringBuffer();
1395: ret.append("LinkedRemoteFile[\"" + this .getName() + "\",");
1396:
1397: if (isFile()) {
1398: ret.append("xfertime:" + _xfertime + ",");
1399: }
1400:
1401: if (isLink()) {
1402: ret.append("link:" + getLinkPath() + ",");
1403: }
1404:
1405: // ret.append(slaves);
1406: if (_slaves != null) {
1407: Iterator i = _slaves.iterator();
1408:
1409: // Enumeration e = slaves.elements();
1410: ret.append("slaves:[");
1411:
1412: // HACK: How can one find out the endpoint without using regexps?
1413: // Pattern p = Pattern.compile("endpoint:\\[(.*?):.*?\\]");
1414: while (i.hasNext()) {
1415: RemoteSlave rslave = (RemoteSlave) i.next();
1416:
1417: if (rslave == null) {
1418: throw new FatalException(
1419: "There's a null in rslaves");
1420: }
1421:
1422: ret.append(rslave.getName());
1423:
1424: if (!rslave.isAvailable()) {
1425: ret.append("-OFFLINE");
1426: }
1427:
1428: if (i.hasNext()) {
1429: ret.append(",");
1430: }
1431: }
1432:
1433: ret.append("]");
1434: }
1435:
1436: if (isDirectory()) {
1437: ret.append("[directory(" + _files.size() + ")]");
1438: }
1439:
1440: ret.append("]");
1441:
1442: return ret.toString();
1443: }
1444:
1445: public synchronized void unmergeDir(RemoteSlave rslave) {
1446: if (!isDirectory()) {
1447: throw new IllegalStateException();
1448: }
1449:
1450: for (Iterator i = new ArrayList(_files.values()).iterator(); i
1451: .hasNext();) {
1452: LinkedRemoteFile file = (LinkedRemoteFile) i.next();
1453:
1454: if (file.isDirectory()) {
1455: boolean wasEmpty = file.isEmpty();
1456: file.unmergeDir(rslave);
1457:
1458: // remove empty directories that used to be non-empty
1459: if (file.isEmpty() && !wasEmpty) {
1460: file.delete();
1461: }
1462: } else if (file.isFile()) {
1463: file.unmergeFile(rslave);
1464: } // else isLink(), we don't remove links
1465: }
1466: }
1467:
1468: public void unmergeFile(RemoteSlave rslave) {
1469: if (!isFile()) {
1470: throw new IllegalStateException();
1471: }
1472:
1473: if (removeSlave(rslave)) {
1474: logger
1475: .warn(getPath() + " deleted from "
1476: + rslave.getName());
1477: }
1478:
1479: // it's safe to remove it as it has no slaves.
1480: // removeSlave() takes care of this
1481: // if (file.getSlaves().size() == 0) {
1482: // i.remove();
1483: // getParentFileNull().addSize(-file.length());
1484: // }
1485: }
1486:
1487: public boolean isDeleted() {
1488: if (isDirectory()) {
1489: return (_parent == null && _name.equals(""));
1490: }
1491:
1492: return _slaves.isEmpty();
1493: }
1494:
1495: public void remerge(CaseInsensitiveHashtable lightRemoteFiles,
1496: RemoteSlave rslave) throws IOException {
1497: if (!isDirectory()) {
1498: throw new RuntimeException(getPath()
1499: + " is not a directory");
1500: }
1501:
1502: for (Iterator iter = new ArrayList<LinkedRemoteFileInterface>(
1503: getFiles2()).iterator(); iter.hasNext();) {
1504: LinkedRemoteFile lrf = (LinkedRemoteFile) iter.next();
1505:
1506: if (lightRemoteFiles.containsKey(lrf.getName())) {
1507: LightRemoteFile light = (LightRemoteFile) lightRemoteFiles
1508: .remove(lrf.getName());
1509: if (light.isDirectory()) {
1510: // the directory still exists, we'll deal with it when
1511: // the slave sends that directory for remerge
1512: continue;
1513: }
1514:
1515: if (light.length() == lrf.length()) {
1516: lrf.addSlave(rslave);
1517: } else { // light.length() != lrf.length()
1518: if (light.length() == 0) {
1519: rslave.simpleDelete(lrf.getPath());
1520: } else if (lrf.getSlaves().size() == 1
1521: && lrf.hasSlave(rslave)) {
1522: lrf.setLength(light.length());
1523: lrf.setCheckSum(0);
1524: } else {
1525: rslave.simpleRename(lrf.getPath(), lrf
1526: .getParentFile().getPath(), lrf
1527: .getName()
1528: + "." + rslave.getName() + ".conflict");
1529: try {
1530: LinkedRemoteFileInterface conflictFile = getFile(light
1531: .getName()
1532: + "."
1533: + rslave.getName()
1534: + ".conflict");
1535: conflictFile.addSlave(rslave);
1536: } catch (FileNotFoundException e) {
1537: ArrayList<RemoteSlave> list = new ArrayList<RemoteSlave>();
1538: list.add(rslave);
1539: addFile(new StaticRemoteFile(list, light
1540: .getName()
1541: + "."
1542: + rslave.getName()
1543: + ".conflict", "drftpd", "drftpd",
1544: light.length(), light
1545: .lastModified()));
1546: }
1547: }
1548: }
1549: } else {
1550: if (lrf.isLink()) {
1551: continue;
1552: } else if (lrf.isFile()) {
1553: lrf.removeSlave(rslave);
1554: } else if (lrf.isDirectory()) {
1555: boolean wasEmpty = lrf.isEmpty();
1556: lrf.unmergeDir(rslave);
1557: if (lrf.isEmpty() && !wasEmpty) {
1558: lrf.delete();
1559: if (isEmpty()) {
1560: delete();
1561: }
1562: }
1563: }
1564: }
1565: }
1566:
1567: for (Iterator iter = lightRemoteFiles.values().iterator(); iter
1568: .hasNext();) {
1569: LightRemoteFile light = (LightRemoteFile) iter.next();
1570: if (light.isDirectory()) {
1571: // ** new directory **
1572: // createDirectory so when remerge is called later with contents
1573: // the new directory exists
1574: createDirectory(light.getName());
1575: // if we set the directory to be old, when files are added, it will update the time correctly
1576: getFile(light.getName()).setLastModified(0);
1577: logger.debug("adding directory " + light.getName()
1578: + " from " + rslave.getName());
1579: } else {
1580: ArrayList<RemoteSlave> list = new ArrayList<RemoteSlave>();
1581: list.add(rslave);
1582: logger.debug("adding file " + light.getName()
1583: + " from " + rslave.getName());
1584: addFile(new StaticRemoteFile(list, light.getName(),
1585: "drftpd", "drftpd", light.length(), light
1586: .lastModified()));
1587: }
1588: }
1589: }
1590:
1591: public static class NonExistingFile {
1592: private LinkedRemoteFile _file;
1593:
1594: private String _path;
1595:
1596: public NonExistingFile(LinkedRemoteFile file, String path) {
1597: _file = file;
1598: _path = path;
1599: }
1600:
1601: /**
1602: * Return true if getPath() returns a null value, i.e. <!-- --> returns
1603: * true if the file exists.
1604: */
1605: public boolean exists() {
1606: return _path == null;
1607: }
1608:
1609: public LinkedRemoteFile getFile() {
1610: return _file;
1611: }
1612:
1613: public String getPath() {
1614: return _path;
1615: }
1616:
1617: public String toString() {
1618: return "[NonExistingFile:file=" + getFile().getPath()
1619: + ",path=" + getPath() + "]";
1620: }
1621: }
1622:
1623: public static LinkedRemoteFile findLatestDir(
1624: ConnectionManager conn, LinkedRemoteFileInterface dir,
1625: User user, String searchstring)
1626: throws ObjectNotFoundException {
1627: ArrayList<LinkedRemoteFile> dirs = findDirs(conn, dir, user,
1628: searchstring);
1629: if (dirs.size() == 0) {
1630: throw new ObjectNotFoundException("dirs has size 0");
1631: }
1632: Collections.sort(dirs, new TimeComparator());
1633: return dirs.get(0);
1634: }
1635:
1636: private static ArrayList<LinkedRemoteFile> findDirs(
1637: ConnectionManager conn, LinkedRemoteFileInterface dir,
1638: User user, String searchstring) {
1639: ArrayList<LinkedRemoteFile> matchingDirs = new ArrayList<LinkedRemoteFile>();
1640:
1641: if (!conn.getGlobalContext().getConfig().checkPathPermission(
1642: "privpath", user, dir, true)) {
1643: logger.debug("privpath: " + dir.getPath());
1644: return matchingDirs;
1645: }
1646:
1647: for (Iterator<LinkedRemoteFileInterface> iter = dir
1648: .getDirectories().iterator(); iter.hasNext();) {
1649: LinkedRemoteFileInterface file = iter.next();
1650: if (file.isDirectory()) {
1651: if (file.getName().toLowerCase().equals(
1652: searchstring.toLowerCase())) {
1653: logger.info("Found " + file.getPath());
1654: // The below cast is a hack, LinkedRemoteFileInterface needs
1655: // to be Comparable
1656: try {
1657: matchingDirs.add((LinkedRemoteFile) file);
1658: } catch (ClassCastException e) {
1659: // must be testing since LinkedRemoteFile is the only
1660: // non-testing class that implements
1661: // LinkedRemoteFileInterface
1662: continue;
1663: }
1664: }
1665: matchingDirs.addAll(findDirs(conn, file, user,
1666: searchstring));
1667: }
1668: }
1669: return matchingDirs;
1670: }
1671:
1672: public List<LinkedRemoteFileInterface> getAllParentFiles() {
1673: List<LinkedRemoteFileInterface> parents = new ArrayList<LinkedRemoteFileInterface>();
1674: parents.add(this );
1675: LinkedRemoteFileInterface parent = this ;
1676: while (true) {
1677: try {
1678: parents.add(parent = parent.getParentFile());
1679: } catch (FileNotFoundException e) {
1680: break;
1681: }
1682: }
1683: return parents;
1684: }
1685: }
|