0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson
0028: */
0029:
0030: package com.caucho.vfs;
0031:
0032: import com.caucho.util.CharBuffer;
0033: import com.caucho.util.Crc64;
0034: import com.caucho.util.L10N;
0035: import com.caucho.util.LruCache;
0036: import com.caucho.util.RandomUtil;
0037:
0038: import java.io.File;
0039: import java.io.IOException;
0040: import java.io.OutputStream;
0041: import java.security.cert.Certificate;
0042: import java.util.ArrayList;
0043: import java.util.Iterator;
0044: import java.util.Map;
0045:
0046: /**
0047: * A virtual filesystem path, essentially represented by a URL.
0048: * Its API resembles a combination of the JDK File object and the URL object.
0049: *
0050: * <p>Paths are, in general, given with the canonical file separator of
0051: * forward slash, '/'. The filesystems will take care of any necessary
0052: * translation.
0053: *
0054: * <p>Currently available filesystems:
0055: * <dl>
0056: * <dt>file:/path/to/file<dd>Java file
0057: * <dt>http://host:port/path/name?query<dd>HTTP request
0058: * <dt>tcp://host:port<dd>Raw TCP connection
0059: * <dt>mailto:user@host?subject=foo&cc=user2<dd>Mail to a user.
0060: * <dt>log:/group/subgroup/item<dd>Logging based on the configuration file.
0061: * <dt>stdout:<dd>System.out
0062: * <dt>stderr:<dd>System.err
0063: * <dt>null:<dd>The equivalent of /dev/null
0064: * </dl>
0065: */
0066: public abstract class Path {
0067: protected final static L10N L = new L10N(Path.class);
0068:
0069: private static final Integer LOCK = new Integer(0);
0070:
0071: private static final LruCache<PathKey, Path> _pathLookupCache = new LruCache<PathKey, Path>(
0072: 8192);
0073:
0074: private static boolean _isTestWindows;
0075:
0076: protected static char _separatorChar = File.separatorChar;
0077: protected static char _pathSeparatorChar = File.pathSeparatorChar;
0078: private static String _newline;
0079:
0080: private static final PathKey _key = new PathKey();
0081:
0082: private static final SchemeMap DEFAULT_SCHEME_MAP = new SchemeMap();
0083:
0084: private static SchemeMap _defaultSchemeMap;
0085:
0086: protected SchemeMap _schemeMap = _defaultSchemeMap;
0087:
0088: /**
0089: * Creates a new Path object.
0090: *
0091: * @param root the new Path root.
0092: */
0093: protected Path(Path root) {
0094: if (root != null)
0095: _schemeMap = root._schemeMap;
0096: else if (_defaultSchemeMap != null)
0097: _schemeMap = _defaultSchemeMap;
0098: else
0099: _schemeMap = DEFAULT_SCHEME_MAP;
0100: }
0101:
0102: /**
0103: * Looks up a new path based on the old path.
0104: *
0105: * @param name relative url to the new path
0106: * @return The new path.
0107: */
0108: public final Path lookup(String name) {
0109: return lookup(name, null);
0110: }
0111:
0112: /**
0113: * Returns a new path relative to the current one.
0114: *
0115: * <p>Path only handles scheme:xxx. Subclasses of Path will specialize
0116: * the xxx.
0117: *
0118: * @param userPath relative or absolute path, essentially any url.
0119: * @param newAttributes attributes for the new path.
0120: *
0121: * @return the new path or null if the scheme doesn't exist
0122: */
0123: public Path lookup(String userPath,
0124: Map<String, Object> newAttributes) {
0125: if (newAttributes != null)
0126: return lookupImpl(userPath, newAttributes);
0127: else if (userPath == null)
0128: return this ;
0129:
0130: if (isPathCacheable()) {
0131: synchronized (_key) {
0132: _key.init(this , userPath);
0133:
0134: Path path = _pathLookupCache.get(_key);
0135:
0136: if (path != null) {
0137: return path.cacheCopy();
0138: }
0139: }
0140: }
0141:
0142: Path path = lookupImpl(userPath, null);
0143:
0144: if (_startTime == 0)
0145: _startTime = System.currentTimeMillis();
0146:
0147: /*
0148: if (System.currentTimeMillis() > 15000) {
0149: if (path.getPath().endsWith("UIRepeat.class")) {
0150: Thread.dumpStack();
0151: System.out.println("PATH: " + path);
0152: }
0153: }
0154: */
0155:
0156: if (isPathCacheable()) {
0157: synchronized (_key) {
0158: Path copy = path.cacheCopy();
0159:
0160: if (copy != null) {
0161: _pathLookupCache.putIfNew(new PathKey(this ,
0162: userPath), copy);
0163: }
0164: }
0165: }
0166:
0167: return path;
0168: }
0169:
0170: static long _startTime;
0171:
0172: /**
0173: * Returns true if the path itself is cacheable
0174: */
0175: protected boolean isPathCacheable() {
0176: return false;
0177: }
0178:
0179: /**
0180: * Returns a new path relative to the current one.
0181: *
0182: * <p>Path only handles scheme:xxx. Subclasses of Path will specialize
0183: * the xxx.
0184: *
0185: * @param userPath relative or absolute path, essentially any url.
0186: * @param newAttributes attributes for the new path.
0187: *
0188: * @return the new path or null if the scheme doesn't exist
0189: */
0190: public Path lookupImpl(String userPath,
0191: Map<String, Object> newAttributes) {
0192: if (userPath == null)
0193: return lookupImpl(getPath(), newAttributes);
0194:
0195: String scheme = scanScheme(userPath);
0196:
0197: if (scheme == null)
0198: return schemeWalk(userPath, newAttributes, userPath, 0);
0199:
0200: Path path;
0201:
0202: SchemeMap schemeMap = _schemeMap;
0203:
0204: // Special case to handle the windows special schemes
0205: // c:xxx -> file:/c:xxx
0206: if (isWindows()) {
0207: int length = scheme.length();
0208: int ch;
0209:
0210: if (length == 1
0211: && ('a' <= (ch = scheme.charAt(0)) && ch <= 'z' || 'A' <= ch
0212: && ch <= 'Z')) {
0213: if (_isTestWindows)
0214: return schemeWalk(userPath, newAttributes, "/"
0215: + userPath, 0);
0216:
0217: path = schemeMap.get("file");
0218:
0219: if (path != null)
0220: return path.schemeWalk(userPath, newAttributes, "/"
0221: + userPath, 0);
0222: else
0223: return schemeWalk(userPath, newAttributes, "/"
0224: + userPath, 0);
0225: }
0226: }
0227:
0228: path = schemeMap.get(scheme);
0229:
0230: // assume the foo:bar is a subfile
0231: if (path == null)
0232: return schemeWalk(userPath, newAttributes, userPath, 0);
0233:
0234: return path.schemeWalk(userPath, newAttributes, userPath,
0235: scheme.length() + 1);
0236: }
0237:
0238: /**
0239: * Looks up a path using the local filesystem conventions. e.g. on
0240: * Windows, a name of 'd:\foo\bar\baz.html' will look up the baz.html
0241: * on drive d.
0242: *
0243: * @param name relative url using local filesystem separators.
0244: */
0245: public final Path lookupNative(String name) {
0246: return lookupNative(name, null);
0247: }
0248:
0249: /**
0250: * Looks up a native path, adding attributes.
0251: */
0252: public Path lookupNative(String name, Map<String, Object> attributes) {
0253: return lookup(name, attributes);
0254: }
0255:
0256: /**
0257: * Returns a native path relative to this native path if the passed path
0258: * is relative to this path, or an absolute path if the passed path is not
0259: * relative to this path.
0260: */
0261: public String lookupRelativeNativePath(Path path) {
0262: String this Native = getNativePath();
0263: String pathNative = path.getNativePath();
0264:
0265: if (pathNative.startsWith(this Native)) {
0266: int i = this Native.length();
0267:
0268: while (i < pathNative.length()) {
0269: if (pathNative.charAt(i) != getFileSeparatorChar())
0270: break;
0271:
0272: i++;
0273: }
0274:
0275: return i == pathNative.length() ? "" : pathNative
0276: .substring(i);
0277: } else
0278: return pathNative;
0279: }
0280:
0281: /**
0282: * Looks up all the resources matching a name. (Generally only useful
0283: * with MergePath.
0284: */
0285: public ArrayList<Path> getResources(String name) {
0286: ArrayList<Path> list = new ArrayList<Path>();
0287: Path path = lookup(name);
0288: if (path.exists())
0289: list.add(path);
0290:
0291: return list;
0292: }
0293:
0294: /**
0295: * Looks up all the existing resources. (Generally only useful
0296: * with MergePath.
0297: */
0298: public ArrayList<Path> getResources() {
0299: ArrayList<Path> list = new ArrayList<Path>();
0300:
0301: //if (exists())
0302: list.add(this );
0303:
0304: return list;
0305: }
0306:
0307: /**
0308: * Returns the parent path.
0309: */
0310: public Path getParent() {
0311: return this ;
0312: }
0313:
0314: /**
0315: * Returns the scheme portion of a uri. Since schemes are case-insensitive,
0316: * normalize them to lower case.
0317: */
0318: protected String scanScheme(String uri) {
0319: int i = 0;
0320: if (uri == null)
0321: return null;
0322:
0323: int length = uri.length();
0324: if (length == 0)
0325: return null;
0326:
0327: int ch = uri.charAt(0);
0328: if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z') {
0329: for (i = 1; i < length; i++) {
0330: ch = uri.charAt(i);
0331:
0332: if (ch == ':')
0333: return uri.substring(0, i).toLowerCase();
0334:
0335: if (!(ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z'
0336: || ch >= '0' && ch <= '0' || ch == '+'
0337: || ch == '-' || ch == '.'))
0338: break;
0339: }
0340: }
0341:
0342: return null;
0343: }
0344:
0345: /**
0346: * Path-specific lookup. Path implementations will override this.
0347: *
0348: * @param userPath the user's lookup() path.
0349: * @param newAttributes the attributes for the new path.
0350: * @param newPath the lookup() path
0351: * @param offset offset into newPath to start lookup.
0352: *
0353: * @return the found path
0354: */
0355: abstract protected Path schemeWalk(String userPath,
0356: Map<String, Object> newAttributes, String newPath,
0357: int offset);
0358:
0359: /**
0360: * Returns the full url for the given path.
0361: */
0362: public String getURL() {
0363: return escapeURL(getScheme() + ":" + getFullPath());
0364: }
0365:
0366: /**
0367: * Returns the url scheme
0368: */
0369: public abstract String getScheme();
0370:
0371: /**
0372: * Returns the hostname
0373: */
0374: public String getHost() {
0375: throw new UnsupportedOperationException();
0376: }
0377:
0378: /**
0379: * Returns the port.
0380: */
0381: public int getPort() {
0382: throw new UnsupportedOperationException();
0383: }
0384:
0385: /**
0386: * Returns the path. e.g. for HTTP, returns the part after the
0387: * host and port.
0388: */
0389: public abstract String getPath();
0390:
0391: /**
0392: * Returns the last segment of the path.
0393: *
0394: * <p>e.g. for http://www.caucho.com/products/index.html, getTail()
0395: * returns 'index.html'
0396: */
0397: public String getTail() {
0398: return "";
0399: }
0400:
0401: /**
0402: * Returns the query string of the path.
0403: */
0404: public String getQuery() {
0405: throw new UnsupportedOperationException();
0406: }
0407:
0408: /**
0409: * Returns the native representation of the path.
0410: *
0411: * On Windows, getNativePath() returns 'd:\\foo\bar.html',
0412: * getPath() returns '/d:/foo/bar.html'
0413: */
0414: public String getNativePath() {
0415: return getFullPath();
0416: }
0417:
0418: /**
0419: * Returns the last string used as a lookup, if available. This allows
0420: * parsers to give intelligent error messages, with the user's path
0421: * instead of the whole path.
0422: *
0423: * The following will print '../test.html':
0424: * <code><pre>
0425: * Path path = Pwd.lookup("/some/dir").lookup("../test.html");
0426: * System.out.println(path.getUserPath());
0427: * </pre></code>
0428: *
0429: */
0430: public String getUserPath() {
0431: return getPath();
0432: }
0433:
0434: /**
0435: * Sets the user path. Useful for temporary files caching another
0436: * URL.
0437: */
0438: public void setUserPath(String userPath) {
0439: }
0440:
0441: /**
0442: * Returns the full path, including the restricted root.
0443: *
0444: * <p>For the following, path.getPath() returns '/file.html', while
0445: * path.getFullPath() returns '/chroot/file.html'.
0446: * <code><pre>
0447: * Path chroot = Pwd.lookup("/chroot").createRoot();
0448: * Path path = chroot.lookup("/file.html");
0449: * </pre></code>
0450: */
0451: public String getFullPath() {
0452: return getPath();
0453: }
0454:
0455: /**
0456: * For union paths like MergePath, return the relative path into
0457: * that path.
0458: */
0459: public String getRelativePath() {
0460: return getPath();
0461: }
0462:
0463: /**
0464: * Returns true for windows security issues.
0465: */
0466: public boolean isWindowsInsecure() {
0467: String lower = getPath().toLowerCase();
0468:
0469: int lastCh;
0470:
0471: if ((lastCh = lower.charAt(lower.length() - 1)) == '.'
0472: || lastCh == ' '
0473: || lastCh == '*'
0474: || lastCh == '?'
0475: || ((lastCh == '/' || lastCh == '\\') && !isDirectory())
0476: || lower.endsWith("::$data")
0477: || isWindowsSpecial(lower, "/con")
0478: || isWindowsSpecial(lower, "/aux")
0479: || isWindowsSpecial(lower, "/prn")
0480: || isWindowsSpecial(lower, "/nul")
0481: || isWindowsSpecial(lower, "/com1")
0482: || isWindowsSpecial(lower, "/com2")
0483: || isWindowsSpecial(lower, "/com3")
0484: || isWindowsSpecial(lower, "/com4")
0485: || isWindowsSpecial(lower, "/lpt1")
0486: || isWindowsSpecial(lower, "/lpt2")
0487: || isWindowsSpecial(lower, "/lpt3")) {
0488: return true;
0489: }
0490:
0491: return false;
0492: }
0493:
0494: private boolean isWindowsSpecial(String lower, String test) {
0495: int p = lower.indexOf(test);
0496:
0497: if (p < 0)
0498: return false;
0499:
0500: int lowerLen = lower.length();
0501: int testLen = test.length();
0502: char ch;
0503:
0504: if (lowerLen == p + testLen
0505: || (ch = lower.charAt(p + testLen)) == '/' || ch == '.')
0506: return true;
0507: else
0508: return false;
0509: }
0510:
0511: /**
0512: * Returns any signing certificates, e.g. for jar signing.
0513: */
0514: public Certificate[] getCertificates() {
0515: return null;
0516: }
0517:
0518: /**
0519: * Tests if the file exists.
0520: */
0521: public boolean exists() {
0522: return false;
0523: }
0524:
0525: /**
0526: * Returns the mime-type of the file.
0527: * <p>Mime-type ignorant filesystems return 'application/octet-stream'
0528: */
0529: public String getContentType() {
0530: return "application/octet-stream";
0531: }
0532:
0533: /**
0534: * Tests if the path refers to a directory.
0535: */
0536: public boolean isDirectory() {
0537: return false;
0538: }
0539:
0540: /**
0541: * Tests if the path refers to a file.
0542: */
0543: public boolean isFile() {
0544: return false;
0545: }
0546:
0547: /**
0548: * Tests if the path refers to a symbolic link.
0549: */
0550: public boolean isLink() {
0551: return false;
0552: }
0553:
0554: /**
0555: * Tests if the path refers to a socket.
0556: */
0557: public boolean isSocket() {
0558: return false;
0559: }
0560:
0561: /**
0562: * Tests if the path refers to a FIFO.
0563: */
0564: public boolean isFIFO() {
0565: return false;
0566: }
0567:
0568: /**
0569: * Tests if the path refers to a block device.
0570: */
0571: public boolean isBlockDevice() {
0572: return false;
0573: }
0574:
0575: /**
0576: * Tests if the path refers to a block device.
0577: */
0578: public boolean isCharacterDevice() {
0579: return false;
0580: }
0581:
0582: /**
0583: * Tests if the path is marked as executable
0584: */
0585: public boolean isExecutable() {
0586: return false;
0587: }
0588:
0589: /**
0590: * Change the executable status of the of the oath.
0591: *
0592: * @throws UnsupportedOperationException
0593: */
0594: public boolean setExecutable(boolean isExecutable) {
0595: return false;
0596: }
0597:
0598: /**
0599: * Tests if the path refers to a symbolic link.
0600: */
0601: public boolean isSymbolicLink() {
0602: return false;
0603: }
0604:
0605: /**
0606: * Tests if the path refers to a hard link.
0607: */
0608: public boolean isHardLink() {
0609: return false;
0610: }
0611:
0612: /**
0613: * Tests if the path refers to an object.
0614: */
0615: public boolean isObject() {
0616: return false;
0617: }
0618:
0619: /**
0620: * Returns the length of the file in bytes.
0621: * @return 0 for non-files
0622: */
0623: public long getLength() {
0624: return 0;
0625: }
0626:
0627: /**
0628: * Returns the last modified time of the file. According to the jdk,
0629: * this may not correspond to the system time.
0630: * @return 0 for non-files.
0631: */
0632: public long getLastModified() {
0633: return 0;
0634: }
0635:
0636: public void setLastModified(long time) {
0637: }
0638:
0639: /**
0640: * Returns the last access time of the file.
0641: *
0642: * @return 0 for non-files.
0643: */
0644: public long getLastAccessTime() {
0645: return getLastModified();
0646: }
0647:
0648: /**
0649: * Returns the create time of the file.
0650: *
0651: * @return 0 for non-files.
0652: */
0653: public long getCreateTime() {
0654: return getLastModified();
0655: }
0656:
0657: /**
0658: * Tests if the file can be read.
0659: */
0660: public boolean canRead() {
0661: return false;
0662: }
0663:
0664: /**
0665: * Tests if the file can be written.
0666: */
0667: public boolean canWrite() {
0668: return false;
0669: }
0670:
0671: //
0672: // POSIX stat() related calls
0673: //
0674:
0675: /**
0676: * Returns equivalent of struct stat.st_dev if appropriate.
0677: */
0678: public long getDevice() {
0679: return 0;
0680: }
0681:
0682: /**
0683: * Returns equivalent of struct stat.st_ino if appropriate.
0684: */
0685: public long getInode() {
0686: return 0;
0687: }
0688:
0689: /**
0690: * Returns equivalent of struct stat.st_mode if appropriate.
0691: */
0692: public int getMode() {
0693: return 0;
0694: }
0695:
0696: /**
0697: * Returns equivalent of struct stat.st_nlink if appropriate.
0698: */
0699: public int getNumberOfLinks() {
0700: return 0;
0701: }
0702:
0703: /**
0704: * Returns equivalent of struct stat.st_uid if appropriate.
0705: */
0706: public int getUser() {
0707: return 0;
0708: }
0709:
0710: /**
0711: * Returns equivalent of struct stat.st_gid if appropriate.
0712: */
0713: public int getGroup() {
0714: return 0;
0715: }
0716:
0717: /**
0718: * Returns equivalent of struct stat.st_rdev if appropriate.
0719: */
0720: public long getDeviceId() {
0721: return 0;
0722: }
0723:
0724: /**
0725: * Returns equivalent of struct stat.st_blksize if appropriate.
0726: */
0727: public long getBlockSize() {
0728: return 0;
0729: }
0730:
0731: /**
0732: * Returns equivalent of struct stat.st_blocks if appropriate.
0733: */
0734: public long getBlockCount() {
0735: return 0;
0736: }
0737:
0738: /**
0739: * Returns equivalent of struct stat.st_ctime if appropriate.
0740: */
0741: public long getLastStatusChangeTime() {
0742: return 0;
0743: }
0744:
0745: /**
0746: * Tests if the file can be read.
0747: */
0748: public boolean canExecute() {
0749: return canRead();
0750: }
0751:
0752: /**
0753: * Changes the group
0754: */
0755: public boolean changeGroup(int gid) throws IOException {
0756: return false;
0757: }
0758:
0759: /**
0760: * Changes the group
0761: */
0762: public boolean changeGroup(String groupName) throws IOException {
0763: return false;
0764: }
0765:
0766: /**
0767: * Changes the permissions
0768: *
0769: * @return true if successful
0770: */
0771: public boolean chmod(int value) {
0772: return false;
0773: }
0774:
0775: public int getOwner() {
0776: return 0;
0777: }
0778:
0779: /**
0780: * Changes the owner
0781: *
0782: * @return true if successful
0783: */
0784: public boolean changeOwner(int uid) throws IOException {
0785: return false;
0786: }
0787:
0788: /**
0789: * Changes the owner
0790: *
0791: * @return true if successful
0792: */
0793: public boolean changeOwner(String ownerName) throws IOException {
0794: return false;
0795: }
0796:
0797: public long getDiskSpaceFree() {
0798: return 0;
0799: }
0800:
0801: public long getDiskSpaceTotal() {
0802: return 0;
0803: }
0804:
0805: /**
0806: * @return The contents of this directory or null if the path does not
0807: * refer to a directory.
0808: */
0809: public String[] list() throws IOException {
0810: return new String[0];
0811: }
0812:
0813: /**
0814: * Returns a jdk1.2 Iterator for the contents of this directory.
0815: */
0816: public Iterator<String> iterator() throws IOException {
0817: String list[] = list();
0818:
0819: // Avoids NPE when subclasses override list() and
0820: // possibly return null, e.g. JarPath.
0821: if (list == null)
0822: list = new String[0];
0823:
0824: return new ArrayIterator(list);
0825: }
0826:
0827: /**
0828: * Creates the directory named by this path.
0829: * @return true if successful.
0830: */
0831: public boolean mkdir() throws IOException {
0832: return false;
0833: }
0834:
0835: /**
0836: * Creates the directory named by this path and any parent directories.
0837: * @return true if successful.
0838: */
0839: public boolean mkdirs() throws IOException {
0840: return false;
0841: }
0842:
0843: /**
0844: * Removes the file or directory named by this path.
0845: *
0846: * @return true if successful
0847: */
0848: public boolean remove() throws IOException {
0849: return false;
0850: }
0851:
0852: /**
0853: * Removes the all files and directories below this path.
0854: *
0855: * @return true if successful.
0856: */
0857: public boolean removeAll() throws IOException {
0858: if (isDirectory()) {
0859: String[] list = list();
0860:
0861: for (int i = 0; i < list.length; i++) {
0862: Path subpath = lookup(list[i]);
0863: subpath.removeAll();
0864: }
0865: }
0866:
0867: return remove();
0868: }
0869:
0870: /**
0871: * Sets the length of the file to zero.
0872: *
0873: * @return true if successful
0874: */
0875: public boolean truncate() throws IOException {
0876: return truncate(0);
0877: }
0878:
0879: /**
0880: * Sets the length of the file.
0881: *
0882: * @return true if successful
0883: */
0884: public boolean truncate(long length) throws IOException {
0885: if (length == 0) {
0886: if (exists()) {
0887: StreamImpl stream = openWriteImpl();
0888: stream.close();
0889:
0890: return true;
0891: } else
0892: return false;
0893: } else
0894: throw new UnsupportedOperationException(getClass()
0895: .getName()
0896: + ": truncate");
0897: }
0898:
0899: /**
0900: * Renames the file or directory to the name given by the path.
0901: * @return true if successful
0902: */
0903: public boolean renameTo(Path path) throws IOException {
0904: return false;
0905: }
0906:
0907: /**
0908: * Renames the file or directory to the name given by the path.
0909: * @return true if successful
0910: */
0911: public final boolean renameTo(String path) throws IOException {
0912: return renameTo(lookup(path));
0913: }
0914:
0915: /**
0916: * Creates a restricted root, like the Unix chroot call.
0917: * Restricted roots cannot access schemes, so file:/etc/passwd cannot
0918: * be used.
0919: *
0920: * <p>createRoot is useful for restricting JavaScript scripts without
0921: * resorting to the dreadfully slow security manager.
0922: */
0923: public Path createRoot() {
0924: return createRoot(SchemeMap.getNullSchemeMap());
0925: }
0926:
0927: public Path createRoot(SchemeMap schemeMap) {
0928: throw new UnsupportedOperationException("createRoot");
0929: }
0930:
0931: /**
0932: * Binds the context to the current path. Later lookups will return
0933: * the new context instead of the current path. Essentially, this is a
0934: * software symbolic link.
0935: */
0936: public void bind(Path context) {
0937: throw new UnsupportedOperationException("bind");
0938: }
0939:
0940: /**
0941: * unbinds a link.
0942: */
0943: public void unbind() {
0944: throw new UnsupportedOperationException("unbind");
0945: }
0946:
0947: /**
0948: * Gets the object at the path. Normal filesystems will generally
0949: * typically return null.
0950: *
0951: * <p>A bean filesystem or a mime-type aware filesystem could deserialize
0952: * the contents of the file.
0953: */
0954: public Object getValue() throws Exception {
0955: throw new UnsupportedOperationException("getValue");
0956: }
0957:
0958: /**
0959: * Sets the object at the path.
0960: *
0961: * <p>Normal filesystems will generally do nothing. However, a bean
0962: * filesystem or a mime-type aware filesystem could serialize the object
0963: * and store it.
0964: */
0965: public void setValue(Object obj) throws Exception {
0966: throw new UnsupportedOperationException("setValue");
0967: }
0968:
0969: /**
0970: * Gets an attribute of the object.
0971: */
0972: public Object getAttribute(String name) throws IOException {
0973: return null;
0974: }
0975:
0976: /**
0977: * Returns a iterator of all attribute names set for this object.
0978: * @return null if path has no attributes.
0979: */
0980: public Iterator getAttributeNames() throws IOException {
0981: return null;
0982: }
0983:
0984: /**
0985: * Opens a resin ReadStream for reading.
0986: */
0987: public final ReadStream openRead() throws IOException {
0988: StreamImpl impl = openReadImpl();
0989: impl.setPath(this );
0990:
0991: return new ReadStream(impl);
0992: }
0993:
0994: /**
0995: * Opens a resin WriteStream for writing.
0996: */
0997: public final WriteStream openWrite() throws IOException {
0998: StreamImpl impl = openWriteImpl();
0999: impl.setPath(this );
1000: return new WriteStream(impl);
1001: }
1002:
1003: /**
1004: * Opens a resin ReadWritePair for reading and writing.
1005: *
1006: * <p>A chat channel, for example, would open its socket using this
1007: * interface.
1008: */
1009: public ReadWritePair openReadWrite() throws IOException {
1010: StreamImpl impl = openReadWriteImpl();
1011: impl.setPath(this );
1012: WriteStream writeStream = new WriteStream(impl);
1013: ReadStream readStream = new ReadStream(impl, writeStream);
1014: return new ReadWritePair(readStream, writeStream);
1015: }
1016:
1017: /**
1018: * Opens a resin ReadWritePair for reading and writing.
1019: *
1020: * <p>A chat channel, for example, would open its socket using this
1021: * interface.
1022: *
1023: * @param is pre-allocated ReadStream to be initialized
1024: * @param os pre-allocated WriteStream to be initialized
1025: */
1026: public void openReadWrite(ReadStream is, WriteStream os)
1027: throws IOException {
1028: StreamImpl impl = openReadWriteImpl();
1029: impl.setPath(this );
1030:
1031: os.init(impl);
1032: is.init(impl, os);
1033: }
1034:
1035: /**
1036: * Opens a resin stream for appending.
1037: */
1038: public WriteStream openAppend() throws IOException {
1039: StreamImpl impl = openAppendImpl();
1040: return new WriteStream(impl);
1041: }
1042:
1043: /**
1044: * Opens a random-access stream.
1045: */
1046: public RandomAccessStream openRandomAccess() throws IOException {
1047: throw new UnsupportedOperationException(getClass().getName());
1048: }
1049:
1050: /**
1051: * Creates the file named by this Path and returns true if the
1052: * file is new.
1053: */
1054: public boolean createNewFile() throws IOException {
1055: synchronized (LOCK) {
1056: if (!exists()) {
1057: WriteStream s = openWrite();
1058: s.close();
1059: return true;
1060: }
1061: }
1062:
1063: return false;
1064: }
1065:
1066: /**
1067: * Creates a dependency.
1068: */
1069: public PersistentDependency createDepend() {
1070: return new Depend(this );
1071: }
1072:
1073: /**
1074: * Creates a unique temporary file as a child of this directory.
1075: *
1076: * @param prefix filename prefix
1077: * @param suffix filename suffix, defaults to .tmp
1078: * @return Path to the new file.
1079: */
1080: public Path createTempFile(String prefix, String suffix)
1081: throws IOException {
1082: if (prefix == null || prefix.length() < 3)
1083: throw new IllegalArgumentException("prefix too short: "
1084: + prefix);
1085:
1086: if (suffix == null)
1087: suffix = ".tmp";
1088:
1089: synchronized (LOCK) {
1090: for (int i = 0; i < 32768; i++) {
1091: int r = Math.abs((int) RandomUtil.getRandomLong());
1092: Path file = lookup(prefix + r + suffix);
1093:
1094: if (file.createNewFile())
1095: return file;
1096: }
1097: }
1098:
1099: throw new IOException("cannot create temp file");
1100: }
1101:
1102: /**
1103: * Creates a link named by this path to another path.
1104: *
1105: * @param target the target of the link
1106: * @param hardLink true if the link should be a hard link
1107: */
1108: public boolean createLink(Path target, boolean hardLink)
1109: throws IOException {
1110: throw new UnsupportedOperationException(getScheme()
1111: + ": doesn't support createLink");
1112: }
1113:
1114: /**
1115: * Utility to write the contents of this path to the destination stream.
1116: *
1117: * @param os destination stream.
1118: */
1119: public void writeToStream(OutputStream os) throws IOException {
1120: StreamImpl is = openReadImpl();
1121: TempBuffer tempBuffer = TempBuffer.allocate();
1122: try {
1123: byte[] buffer = tempBuffer.getBuffer();
1124: int length = buffer.length;
1125: int len;
1126:
1127: while ((len = is.read(buffer, 0, length)) > 0)
1128: os.write(buffer, 0, len);
1129: } finally {
1130: TempBuffer.free(tempBuffer);
1131: tempBuffer = null;
1132:
1133: is.close();
1134: }
1135: }
1136:
1137: /**
1138: * Utility to write the contents of this path to the destination stream.
1139: *
1140: * @param os destination stream.
1141: */
1142: public void writeToStream(OutputStreamWithBuffer os)
1143: throws IOException {
1144: StreamImpl is = openReadImpl();
1145:
1146: try {
1147: byte[] buffer = os.getBuffer();
1148: int offset = os.getBufferOffset();
1149: int length = buffer.length;
1150:
1151: while (true) {
1152: int sublen = length - offset;
1153:
1154: if (sublen <= 0) {
1155: buffer = os.nextBuffer(offset);
1156: offset = 0;
1157: sublen = length;
1158: }
1159:
1160: sublen = is.read(buffer, offset, sublen);
1161:
1162: if (sublen <= 0) {
1163: os.setBufferOffset(offset);
1164: return;
1165: }
1166:
1167: offset += sublen;
1168: }
1169: } finally {
1170: is.close();
1171: }
1172: }
1173:
1174: /**
1175: * Returns the crc64 code.
1176: */
1177: public long getCrc64() {
1178: try {
1179: if (isDirectory()) {
1180: String[] list = list();
1181:
1182: long digest = 0;
1183:
1184: for (int i = 0; i < list.length; i++) {
1185: digest = Crc64.generate(digest, list[i]);
1186: }
1187:
1188: return digest;
1189: } else if (canRead()) {
1190: ReadStream is = openRead();
1191:
1192: try {
1193: long digest = 0;
1194:
1195: int ch;
1196:
1197: byte[] buffer = is.getBuffer();
1198: while (is.fillBuffer() > 0) {
1199: int length = is.getLength();
1200:
1201: digest = Crc64.generate(digest, buffer, 0,
1202: length);
1203: }
1204:
1205: return digest;
1206: } finally {
1207: is.close();
1208: }
1209: } else {
1210: return -1; // Depend requires -1
1211: }
1212: } catch (IOException e) {
1213: // XXX: log
1214: e.printStackTrace();
1215:
1216: return -1;
1217: }
1218: }
1219:
1220: /**
1221: * Returns the object at this path. Normally, only paths like JNDI
1222: * will support this.
1223: */
1224: public Object getObject() throws IOException {
1225: throw new UnsupportedOperationException(getScheme()
1226: + ": doesn't support getObject");
1227: }
1228:
1229: /**
1230: * Sets the object at this path. Normally, only paths like JNDI
1231: * will support this.
1232: */
1233: public void setObject(Object obj) throws IOException {
1234: throw new UnsupportedOperationException(getScheme()
1235: + ": doesn't support setObject");
1236: }
1237:
1238: public int hashCode() {
1239: return toString().hashCode();
1240: }
1241:
1242: public boolean equals(Object o) {
1243: if (this == o)
1244: return true;
1245: else if (!(o instanceof Path))
1246: return false;
1247: else
1248: return getURL().equals(((Path) o).getURL());
1249: }
1250:
1251: public String toString() {
1252: return getFullPath();
1253: }
1254:
1255: public StreamImpl openReadImpl() throws IOException {
1256: throw new UnsupportedOperationException("openRead:"
1257: + getClass().getName());
1258: }
1259:
1260: public StreamImpl openWriteImpl() throws IOException {
1261: throw new UnsupportedOperationException("openWrite:"
1262: + getClass().getName());
1263: }
1264:
1265: public StreamImpl openReadWriteImpl() throws IOException {
1266: throw new UnsupportedOperationException("openReadWrite:"
1267: + getClass().getName());
1268: }
1269:
1270: public StreamImpl openAppendImpl() throws IOException {
1271: throw new UnsupportedOperationException("openAppend:"
1272: + getClass().getName());
1273: }
1274:
1275: protected static String escapeURL(String rawURL) {
1276: CharBuffer cb = null;
1277: int length = rawURL.length();
1278:
1279: for (int i = 0; i < length; i++) {
1280: char ch = rawURL.charAt(i);
1281:
1282: switch (ch) {
1283: case ' ':
1284: if (cb == null) {
1285: cb = new CharBuffer();
1286: cb.append(rawURL, 0, i);
1287: }
1288: cb.append("%20");
1289: break;
1290:
1291: case '#':
1292: if (cb == null) {
1293: cb = new CharBuffer();
1294: cb.append(rawURL, 0, i);
1295: }
1296: cb.append("%23");
1297: break;
1298:
1299: case '%':
1300: if (cb == null) {
1301: cb = new CharBuffer();
1302: cb.append(rawURL, 0, i);
1303: }
1304: cb.append("%25");
1305: break;
1306:
1307: default:
1308: if (cb != null)
1309: cb.append(ch);
1310: break;
1311: }
1312: }
1313:
1314: if (cb != null)
1315: return cb.toString();
1316: else
1317: return rawURL;
1318: }
1319:
1320: protected Path copy() {
1321: return this ;
1322: }
1323:
1324: /**
1325: * Copy for caching.
1326: */
1327: protected Path cacheCopy() {
1328: return this ;
1329: }
1330:
1331: public static final void setDefaultSchemeMap(SchemeMap schemeMap) {
1332: _defaultSchemeMap = schemeMap;
1333: _pathLookupCache.clear();
1334: }
1335:
1336: public static final boolean isWindows() {
1337: return _separatorChar == '\\' || _isTestWindows;
1338: }
1339:
1340: public static final void setTestWindows(boolean isTest) {
1341: _isTestWindows = isTest;
1342: }
1343:
1344: protected static final char getSeparatorChar() {
1345: return _separatorChar;
1346: }
1347:
1348: public static final char getFileSeparatorChar() {
1349: return _separatorChar;
1350: }
1351:
1352: public static final char getPathSeparatorChar() {
1353: return _pathSeparatorChar;
1354: }
1355:
1356: protected static String getUserDir() {
1357: return System.getProperty("user.dir");
1358: }
1359:
1360: public static String getNewlineString() {
1361: if (_newline == null) {
1362: _newline = System.getProperty("line.separator");
1363: if (_newline == null)
1364: _newline = "\n";
1365: }
1366:
1367: return _newline;
1368: }
1369:
1370: private class ArrayIterator implements Iterator<String> {
1371: String[] list;
1372: int index;
1373:
1374: public boolean hasNext() {
1375: return index < list.length;
1376: }
1377:
1378: public String next() {
1379: return index < list.length ? list[index++] : null;
1380: }
1381:
1382: public void remove() {
1383: throw new UnsupportedOperationException();
1384: }
1385:
1386: ArrayIterator(String[] list) {
1387: this .list = list;
1388: index = 0;
1389: }
1390: }
1391:
1392: static class PathKey {
1393: private Path _parent;
1394: private String _lookup;
1395:
1396: PathKey() {
1397: }
1398:
1399: PathKey(Path parent, String lookup) {
1400: _parent = parent;
1401: _lookup = lookup;
1402: }
1403:
1404: void init(Path parent, String lookup) {
1405: _parent = parent;
1406: _lookup = lookup;
1407: }
1408:
1409: public int hashCode() {
1410: if (_parent != null)
1411: return _parent.hashCode() * 65521 + _lookup.hashCode();
1412: else
1413: return _lookup.hashCode();
1414: }
1415:
1416: public boolean equals(Object test) {
1417: if (!(test instanceof PathKey))
1418: return false;
1419:
1420: PathKey key = (PathKey) test;
1421:
1422: if (_parent != null)
1423: return (_parent.equals(key._parent) && _lookup
1424: .equals(key._lookup));
1425: else
1426: return (key._parent == null && _lookup
1427: .equals(key._lookup));
1428: }
1429: }
1430:
1431: static {
1432: DEFAULT_SCHEME_MAP.put("file", new FilePath(null));
1433:
1434: //DEFAULT_SCHEME_MAP.put("jar", new JarScheme(null));
1435: DEFAULT_SCHEME_MAP.put("http", new HttpPath("127.0.0.1", 0));
1436: DEFAULT_SCHEME_MAP.put("https", new HttpsPath("127.0.0.1", 0));
1437: DEFAULT_SCHEME_MAP.put("tcp", new TcpPath(null, null, null,
1438: "127.0.0.1", 0));
1439: DEFAULT_SCHEME_MAP.put("tcps", new TcpsPath(null, null, null,
1440: "127.0.0.1", 0));
1441:
1442: StreamImpl stdout = StdoutStream.create();
1443: StreamImpl stderr = StderrStream.create();
1444: DEFAULT_SCHEME_MAP.put("stdout", stdout.getPath());
1445: DEFAULT_SCHEME_MAP.put("stderr", stderr.getPath());
1446: VfsStream nullStream = new VfsStream(null, null);
1447: DEFAULT_SCHEME_MAP.put("null", new ConstPath(null, nullStream));
1448: DEFAULT_SCHEME_MAP.put("jndi", new JndiPath());
1449: }
1450: }
|