0001: /*
0002: * Copyright 1999,2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.apache.naming.resources;
0018:
0019: import java.io.File;
0020: import java.io.FileInputStream;
0021: import java.io.FileOutputStream;
0022: import java.io.IOException;
0023: import java.io.InputStream;
0024: import java.util.Arrays;
0025: import java.util.Date;
0026: import java.util.Hashtable;
0027: import java.util.Vector;
0028:
0029: import javax.naming.NameAlreadyBoundException;
0030: import javax.naming.NamingEnumeration;
0031: import javax.naming.NamingException;
0032: import javax.naming.OperationNotSupportedException;
0033: import javax.naming.directory.Attributes;
0034: import javax.naming.directory.DirContext;
0035: import javax.naming.directory.ModificationItem;
0036: import javax.naming.directory.SearchControls;
0037:
0038: import org.apache.naming.NamingContextBindingsEnumeration;
0039: import org.apache.naming.NamingContextEnumeration;
0040: import org.apache.naming.NamingEntry;
0041:
0042: /**
0043: * Filesystem Directory Context implementation helper class.
0044: *
0045: * @author Remy Maucherat
0046: * @version $Revision: 1.4 $ $Date: 2004/02/27 14:58:54 $
0047: */
0048:
0049: public class FileDirContext extends BaseDirContext {
0050:
0051: // -------------------------------------------------------------- Constants
0052:
0053: /**
0054: * The descriptive information string for this implementation.
0055: */
0056: protected static final int BUFFER_SIZE = 2048;
0057:
0058: // ----------------------------------------------------------- Constructors
0059:
0060: /**
0061: * Builds a file directory context using the given environment.
0062: */
0063: public FileDirContext() {
0064: super ();
0065: }
0066:
0067: /**
0068: * Builds a file directory context using the given environment.
0069: */
0070: public FileDirContext(Hashtable env) {
0071: super (env);
0072: }
0073:
0074: // ----------------------------------------------------- Instance Variables
0075:
0076: /**
0077: * The document base directory.
0078: */
0079: protected File base = null;
0080:
0081: /**
0082: * Absolute normalized filename of the base.
0083: */
0084: protected String absoluteBase = null;
0085:
0086: /**
0087: * Case sensitivity.
0088: */
0089: protected boolean caseSensitive = true;
0090:
0091: /**
0092: * Allow linking.
0093: */
0094: protected boolean allowLinking = false;
0095:
0096: // ------------------------------------------------------------- Properties
0097:
0098: /**
0099: * Set the document root.
0100: *
0101: * @param docBase The new document root
0102: *
0103: * @exception IllegalArgumentException if the specified value is not
0104: * supported by this implementation
0105: * @exception IllegalArgumentException if this would create a
0106: * malformed URL
0107: */
0108: public void setDocBase(String docBase) {
0109:
0110: // Validate the format of the proposed document root
0111: if (docBase == null)
0112: throw new IllegalArgumentException(sm
0113: .getString("resources.null"));
0114:
0115: // Calculate a File object referencing this document base directory
0116: base = new File(docBase);
0117: try {
0118: base = base.getCanonicalFile();
0119: } catch (IOException e) {
0120: // Ignore
0121: }
0122:
0123: // Validate that the document base is an existing directory
0124: if (!base.exists() || !base.isDirectory() || !base.canRead())
0125: throw new IllegalArgumentException(sm.getString(
0126: "fileResources.base", docBase));
0127: this .absoluteBase = base.getAbsolutePath();
0128: super .setDocBase(docBase);
0129:
0130: }
0131:
0132: /**
0133: * Set case sensitivity.
0134: */
0135: public void setCaseSensitive(boolean caseSensitive) {
0136: this .caseSensitive = caseSensitive;
0137: }
0138:
0139: /**
0140: * Is case sensitive ?
0141: */
0142: public boolean isCaseSensitive() {
0143: return caseSensitive;
0144: }
0145:
0146: /**
0147: * Set allow linking.
0148: */
0149: public void setAllowLinking(boolean allowLinking) {
0150: this .allowLinking = allowLinking;
0151: }
0152:
0153: /**
0154: * Is linking allowed.
0155: */
0156: public boolean getAllowLinking() {
0157: return allowLinking;
0158: }
0159:
0160: // --------------------------------------------------------- Public Methods
0161:
0162: /**
0163: * Release any resources allocated for this directory context.
0164: */
0165: public void release() {
0166:
0167: caseSensitive = true;
0168: allowLinking = false;
0169: absoluteBase = null;
0170: base = null;
0171: super .release();
0172:
0173: }
0174:
0175: // -------------------------------------------------------- Context Methods
0176:
0177: /**
0178: * Retrieves the named object.
0179: *
0180: * @param name the name of the object to look up
0181: * @return the object bound to name
0182: * @exception NamingException if a naming exception is encountered
0183: */
0184: public Object lookup(String name) throws NamingException {
0185: Object result = null;
0186: File file = file(name);
0187:
0188: if (file == null)
0189: throw new NamingException(sm.getString(
0190: "resources.notFound", name));
0191:
0192: if (file.isDirectory()) {
0193: FileDirContext tempContext = new FileDirContext(env);
0194: tempContext.setDocBase(file.getPath());
0195: result = tempContext;
0196: } else {
0197: result = new FileResource(file);
0198: }
0199:
0200: return result;
0201:
0202: }
0203:
0204: /**
0205: * Unbinds the named object. Removes the terminal atomic name in name
0206: * from the target context--that named by all but the terminal atomic
0207: * part of name.
0208: * <p>
0209: * This method is idempotent. It succeeds even if the terminal atomic
0210: * name is not bound in the target context, but throws
0211: * NameNotFoundException if any of the intermediate contexts do not exist.
0212: *
0213: * @param name the name to bind; may not be empty
0214: * @exception NameNotFoundException if an intermediate context does not
0215: * exist
0216: * @exception NamingException if a naming exception is encountered
0217: */
0218: public void unbind(String name) throws NamingException {
0219:
0220: File file = file(name);
0221:
0222: if (file == null)
0223: throw new NamingException(sm.getString(
0224: "resources.notFound", name));
0225:
0226: if (!file.delete())
0227: throw new NamingException(sm.getString(
0228: "resources.unbindFailed", name));
0229:
0230: }
0231:
0232: /**
0233: * Binds a new name to the object bound to an old name, and unbinds the
0234: * old name. Both names are relative to this context. Any attributes
0235: * associated with the old name become associated with the new name.
0236: * Intermediate contexts of the old name are not changed.
0237: *
0238: * @param oldName the name of the existing binding; may not be empty
0239: * @param newName the name of the new binding; may not be empty
0240: * @exception NameAlreadyBoundException if newName is already bound
0241: * @exception NamingException if a naming exception is encountered
0242: */
0243: public void rename(String oldName, String newName)
0244: throws NamingException {
0245:
0246: File file = file(oldName);
0247:
0248: if (file == null)
0249: throw new NamingException(sm.getString(
0250: "resources.notFound", oldName));
0251:
0252: File newFile = new File(base, newName);
0253:
0254: file.renameTo(newFile);
0255:
0256: }
0257:
0258: /**
0259: * Enumerates the names bound in the named context, along with the class
0260: * names of objects bound to them. The contents of any subcontexts are
0261: * not included.
0262: * <p>
0263: * If a binding is added to or removed from this context, its effect on
0264: * an enumeration previously returned is undefined.
0265: *
0266: * @param name the name of the context to list
0267: * @return an enumeration of the names and class names of the bindings in
0268: * this context. Each element of the enumeration is of type NameClassPair.
0269: * @exception NamingException if a naming exception is encountered
0270: */
0271: public NamingEnumeration list(String name) throws NamingException {
0272:
0273: File file = file(name);
0274:
0275: if (file == null)
0276: throw new NamingException(sm.getString(
0277: "resources.notFound", name));
0278:
0279: Vector entries = list(file);
0280:
0281: return new NamingContextEnumeration(entries);
0282:
0283: }
0284:
0285: /**
0286: * Enumerates the names bound in the named context, along with the
0287: * objects bound to them. The contents of any subcontexts are not
0288: * included.
0289: * <p>
0290: * If a binding is added to or removed from this context, its effect on
0291: * an enumeration previously returned is undefined.
0292: *
0293: * @param name the name of the context to list
0294: * @return an enumeration of the bindings in this context.
0295: * Each element of the enumeration is of type Binding.
0296: * @exception NamingException if a naming exception is encountered
0297: */
0298: public NamingEnumeration listBindings(String name)
0299: throws NamingException {
0300:
0301: File file = file(name);
0302:
0303: if (file == null)
0304: throw new NamingException(sm.getString(
0305: "resources.notFound", name));
0306:
0307: Vector entries = list(file);
0308:
0309: return new NamingContextBindingsEnumeration(entries);
0310:
0311: }
0312:
0313: /**
0314: * Destroys the named context and removes it from the namespace. Any
0315: * attributes associated with the name are also removed. Intermediate
0316: * contexts are not destroyed.
0317: * <p>
0318: * This method is idempotent. It succeeds even if the terminal atomic
0319: * name is not bound in the target context, but throws
0320: * NameNotFoundException if any of the intermediate contexts do not exist.
0321: *
0322: * In a federated naming system, a context from one naming system may be
0323: * bound to a name in another. One can subsequently look up and perform
0324: * operations on the foreign context using a composite name. However, an
0325: * attempt destroy the context using this composite name will fail with
0326: * NotContextException, because the foreign context is not a "subcontext"
0327: * of the context in which it is bound. Instead, use unbind() to remove
0328: * the binding of the foreign context. Destroying the foreign context
0329: * requires that the destroySubcontext() be performed on a context from
0330: * the foreign context's "native" naming system.
0331: *
0332: * @param name the name of the context to be destroyed; may not be empty
0333: * @exception NameNotFoundException if an intermediate context does not
0334: * exist
0335: * @exception NotContextException if the name is bound but does not name
0336: * a context, or does not name a context of the appropriate type
0337: */
0338: public void destroySubcontext(String name) throws NamingException {
0339: unbind(name);
0340: }
0341:
0342: /**
0343: * Retrieves the named object, following links except for the terminal
0344: * atomic component of the name. If the object bound to name is not a
0345: * link, returns the object itself.
0346: *
0347: * @param name the name of the object to look up
0348: * @return the object bound to name, not following the terminal link
0349: * (if any).
0350: * @exception NamingException if a naming exception is encountered
0351: */
0352: public Object lookupLink(String name) throws NamingException {
0353: // Note : Links are not supported
0354: return lookup(name);
0355: }
0356:
0357: /**
0358: * Retrieves the full name of this context within its own namespace.
0359: * <p>
0360: * Many naming services have a notion of a "full name" for objects in
0361: * their respective namespaces. For example, an LDAP entry has a
0362: * distinguished name, and a DNS record has a fully qualified name. This
0363: * method allows the client application to retrieve this name. The string
0364: * returned by this method is not a JNDI composite name and should not be
0365: * passed directly to context methods. In naming systems for which the
0366: * notion of full name does not make sense,
0367: * OperationNotSupportedException is thrown.
0368: *
0369: * @return this context's name in its own namespace; never null
0370: * @exception OperationNotSupportedException if the naming system does
0371: * not have the notion of a full name
0372: * @exception NamingException if a naming exception is encountered
0373: */
0374: public String getNameInNamespace() throws NamingException {
0375: return docBase;
0376: }
0377:
0378: // ----------------------------------------------------- DirContext Methods
0379:
0380: /**
0381: * Retrieves selected attributes associated with a named object.
0382: * See the class description regarding attribute models, attribute type
0383: * names, and operational attributes.
0384: *
0385: * @return the requested attributes; never null
0386: * @param name the name of the object from which to retrieve attributes
0387: * @param attrIds the identifiers of the attributes to retrieve. null
0388: * indicates that all attributes should be retrieved; an empty array
0389: * indicates that none should be retrieved
0390: * @exception NamingException if a naming exception is encountered
0391: */
0392: public Attributes getAttributes(String name, String[] attrIds)
0393: throws NamingException {
0394:
0395: // Building attribute list
0396: File file = file(name);
0397:
0398: if (file == null)
0399: throw new NamingException(sm.getString(
0400: "resources.notFound", name));
0401:
0402: return new FileResourceAttributes(file);
0403:
0404: }
0405:
0406: /**
0407: * Modifies the attributes associated with a named object. The order of
0408: * the modifications is not specified. Where possible, the modifications
0409: * are performed atomically.
0410: *
0411: * @param name the name of the object whose attributes will be updated
0412: * @param mod_op the modification operation, one of: ADD_ATTRIBUTE,
0413: * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE
0414: * @param attrs the attributes to be used for the modification; may not
0415: * be null
0416: * @exception AttributeModificationException if the modification cannot be
0417: * completed successfully
0418: * @exception NamingException if a naming exception is encountered
0419: */
0420: public void modifyAttributes(String name, int mod_op,
0421: Attributes attrs) throws NamingException {
0422:
0423: }
0424:
0425: /**
0426: * Modifies the attributes associated with a named object using an an
0427: * ordered list of modifications. The modifications are performed in the
0428: * order specified. Each modification specifies a modification operation
0429: * code and an attribute on which to operate. Where possible, the
0430: * modifications are performed atomically.
0431: *
0432: * @param name the name of the object whose attributes will be updated
0433: * @param mods an ordered sequence of modifications to be performed; may
0434: * not be null
0435: * @exception AttributeModificationException if the modification cannot be
0436: * completed successfully
0437: * @exception NamingException if a naming exception is encountered
0438: */
0439: public void modifyAttributes(String name, ModificationItem[] mods)
0440: throws NamingException {
0441:
0442: }
0443:
0444: /**
0445: * Binds a name to an object, along with associated attributes. If attrs
0446: * is null, the resulting binding will have the attributes associated
0447: * with obj if obj is a DirContext, and no attributes otherwise. If attrs
0448: * is non-null, the resulting binding will have attrs as its attributes;
0449: * any attributes associated with obj are ignored.
0450: *
0451: * @param name the name to bind; may not be empty
0452: * @param obj the object to bind; possibly null
0453: * @param attrs the attributes to associate with the binding
0454: * @exception NameAlreadyBoundException if name is already bound
0455: * @exception InvalidAttributesException if some "mandatory" attributes
0456: * of the binding are not supplied
0457: * @exception NamingException if a naming exception is encountered
0458: */
0459: public void bind(String name, Object obj, Attributes attrs)
0460: throws NamingException {
0461:
0462: // Note: No custom attributes allowed
0463:
0464: File file = new File(base, name);
0465: if (file.exists())
0466: throw new NameAlreadyBoundException(sm.getString(
0467: "resources.alreadyBound", name));
0468:
0469: rebind(name, obj, attrs);
0470:
0471: }
0472:
0473: /**
0474: * Binds a name to an object, along with associated attributes,
0475: * overwriting any existing binding. If attrs is null and obj is a
0476: * DirContext, the attributes from obj are used. If attrs is null and obj
0477: * is not a DirContext, any existing attributes associated with the object
0478: * already bound in the directory remain unchanged. If attrs is non-null,
0479: * any existing attributes associated with the object already bound in
0480: * the directory are removed and attrs is associated with the named
0481: * object. If obj is a DirContext and attrs is non-null, the attributes
0482: * of obj are ignored.
0483: *
0484: * @param name the name to bind; may not be empty
0485: * @param obj the object to bind; possibly null
0486: * @param attrs the attributes to associate with the binding
0487: * @exception InvalidAttributesException if some "mandatory" attributes
0488: * of the binding are not supplied
0489: * @exception NamingException if a naming exception is encountered
0490: */
0491: public void rebind(String name, Object obj, Attributes attrs)
0492: throws NamingException {
0493:
0494: // Note: No custom attributes allowed
0495: // Check obj type
0496:
0497: File file = new File(base, name);
0498:
0499: InputStream is = null;
0500: if (obj instanceof Resource) {
0501: try {
0502: is = ((Resource) obj).streamContent();
0503: } catch (IOException e) {
0504: }
0505: } else if (obj instanceof InputStream) {
0506: is = (InputStream) obj;
0507: } else if (obj instanceof DirContext) {
0508: if (file.exists()) {
0509: if (!file.delete())
0510: throw new NamingException(sm.getString(
0511: "resources.bindFailed", name));
0512: }
0513: if (!file.mkdir())
0514: throw new NamingException(sm.getString(
0515: "resources.bindFailed", name));
0516: }
0517: if (is == null)
0518: throw new NamingException(sm.getString(
0519: "resources.bindFailed", name));
0520:
0521: // Open os
0522:
0523: try {
0524: FileOutputStream os = null;
0525: byte buffer[] = new byte[BUFFER_SIZE];
0526: int len = -1;
0527: try {
0528: os = new FileOutputStream(file);
0529: while (true) {
0530: len = is.read(buffer);
0531: if (len == -1)
0532: break;
0533: os.write(buffer, 0, len);
0534: }
0535: } finally {
0536: if (os != null)
0537: os.close();
0538: is.close();
0539: }
0540: } catch (IOException e) {
0541: throw new NamingException(sm.getString(
0542: "resources.bindFailed", e));
0543: }
0544:
0545: }
0546:
0547: /**
0548: * Creates and binds a new context, along with associated attributes.
0549: * This method creates a new subcontext with the given name, binds it in
0550: * the target context (that named by all but terminal atomic component of
0551: * the name), and associates the supplied attributes with the newly
0552: * created object. All intermediate and target contexts must already
0553: * exist. If attrs is null, this method is equivalent to
0554: * Context.createSubcontext().
0555: *
0556: * @param name the name of the context to create; may not be empty
0557: * @param attrs the attributes to associate with the newly created context
0558: * @return the newly created context
0559: * @exception NameAlreadyBoundException if the name is already bound
0560: * @exception InvalidAttributesException if attrs does not contain all
0561: * the mandatory attributes required for creation
0562: * @exception NamingException if a naming exception is encountered
0563: */
0564: public DirContext createSubcontext(String name, Attributes attrs)
0565: throws NamingException {
0566:
0567: File file = new File(base, name);
0568: if (file.exists())
0569: throw new NameAlreadyBoundException(sm.getString(
0570: "resources.alreadyBound", name));
0571: if (!file.mkdir())
0572: throw new NamingException(sm.getString(
0573: "resources.bindFailed", name));
0574: return (DirContext) lookup(name);
0575:
0576: }
0577:
0578: /**
0579: * Retrieves the schema associated with the named object. The schema
0580: * describes rules regarding the structure of the namespace and the
0581: * attributes stored within it. The schema specifies what types of
0582: * objects can be added to the directory and where they can be added;
0583: * what mandatory and optional attributes an object can have. The range
0584: * of support for schemas is directory-specific.
0585: *
0586: * @param name the name of the object whose schema is to be retrieved
0587: * @return the schema associated with the context; never null
0588: * @exception OperationNotSupportedException if schema not supported
0589: * @exception NamingException if a naming exception is encountered
0590: */
0591: public DirContext getSchema(String name) throws NamingException {
0592: throw new OperationNotSupportedException();
0593: }
0594:
0595: /**
0596: * Retrieves a context containing the schema objects of the named
0597: * object's class definitions.
0598: *
0599: * @param name the name of the object whose object class definition is to
0600: * be retrieved
0601: * @return the DirContext containing the named object's class
0602: * definitions; never null
0603: * @exception OperationNotSupportedException if schema not supported
0604: * @exception NamingException if a naming exception is encountered
0605: */
0606: public DirContext getSchemaClassDefinition(String name)
0607: throws NamingException {
0608: throw new OperationNotSupportedException();
0609: }
0610:
0611: /**
0612: * Searches in a single context for objects that contain a specified set
0613: * of attributes, and retrieves selected attributes. The search is
0614: * performed using the default SearchControls settings.
0615: *
0616: * @param name the name of the context to search
0617: * @param matchingAttributes the attributes to search for. If empty or
0618: * null, all objects in the target context are returned.
0619: * @param attributesToReturn the attributes to return. null indicates
0620: * that all attributes are to be returned; an empty array indicates that
0621: * none are to be returned.
0622: * @return a non-null enumeration of SearchResult objects. Each
0623: * SearchResult contains the attributes identified by attributesToReturn
0624: * and the name of the corresponding object, named relative to the
0625: * context named by name.
0626: * @exception NamingException if a naming exception is encountered
0627: */
0628: public NamingEnumeration search(String name,
0629: Attributes matchingAttributes, String[] attributesToReturn)
0630: throws NamingException {
0631: return null;
0632: }
0633:
0634: /**
0635: * Searches in a single context for objects that contain a specified set
0636: * of attributes. This method returns all the attributes of such objects.
0637: * It is equivalent to supplying null as the atributesToReturn parameter
0638: * to the method search(Name, Attributes, String[]).
0639: *
0640: * @param name the name of the context to search
0641: * @param matchingAttributes the attributes to search for. If empty or
0642: * null, all objects in the target context are returned.
0643: * @return a non-null enumeration of SearchResult objects. Each
0644: * SearchResult contains the attributes identified by attributesToReturn
0645: * and the name of the corresponding object, named relative to the
0646: * context named by name.
0647: * @exception NamingException if a naming exception is encountered
0648: */
0649: public NamingEnumeration search(String name,
0650: Attributes matchingAttributes) throws NamingException {
0651: return null;
0652: }
0653:
0654: /**
0655: * Searches in the named context or object for entries that satisfy the
0656: * given search filter. Performs the search as specified by the search
0657: * controls.
0658: *
0659: * @param name the name of the context or object to search
0660: * @param filter the filter expression to use for the search; may not be
0661: * null
0662: * @param cons the search controls that control the search. If null,
0663: * the default search controls are used (equivalent to
0664: * (new SearchControls())).
0665: * @return an enumeration of SearchResults of the objects that satisfy
0666: * the filter; never null
0667: * @exception InvalidSearchFilterException if the search filter specified
0668: * is not supported or understood by the underlying directory
0669: * @exception InvalidSearchControlsException if the search controls
0670: * contain invalid settings
0671: * @exception NamingException if a naming exception is encountered
0672: */
0673: public NamingEnumeration search(String name, String filter,
0674: SearchControls cons) throws NamingException {
0675: return null;
0676: }
0677:
0678: /**
0679: * Searches in the named context or object for entries that satisfy the
0680: * given search filter. Performs the search as specified by the search
0681: * controls.
0682: *
0683: * @param name the name of the context or object to search
0684: * @param filterExpr the filter expression to use for the search.
0685: * The expression may contain variables of the form "{i}" where i is a
0686: * nonnegative integer. May not be null.
0687: * @param filterArgs the array of arguments to substitute for the
0688: * variables in filterExpr. The value of filterArgs[i] will replace each
0689: * occurrence of "{i}". If null, equivalent to an empty array.
0690: * @param cons the search controls that control the search. If null, the
0691: * default search controls are used (equivalent to (new SearchControls())).
0692: * @return an enumeration of SearchResults of the objects that satisy the
0693: * filter; never null
0694: * @exception ArrayIndexOutOfBoundsException if filterExpr contains {i}
0695: * expressions where i is outside the bounds of the array filterArgs
0696: * @exception InvalidSearchControlsException if cons contains invalid
0697: * settings
0698: * @exception InvalidSearchFilterException if filterExpr with filterArgs
0699: * represents an invalid search filter
0700: * @exception NamingException if a naming exception is encountered
0701: */
0702: public NamingEnumeration search(String name, String filterExpr,
0703: Object[] filterArgs, SearchControls cons)
0704: throws NamingException {
0705: return null;
0706: }
0707:
0708: // ------------------------------------------------------ Protected Methods
0709:
0710: /**
0711: * Return a context-relative path, beginning with a "/", that represents
0712: * the canonical version of the specified path after ".." and "." elements
0713: * are resolved out. If the specified path attempts to go outside the
0714: * boundaries of the current context (i.e. too many ".." path elements
0715: * are present), return <code>null</code> instead.
0716: *
0717: * @param path Path to be normalized
0718: */
0719: protected String normalize(String path) {
0720:
0721: String normalized = path;
0722:
0723: // Normalize the slashes and add leading slash if necessary
0724: if (normalized.indexOf('\\') >= 0)
0725: normalized = normalized.replace('\\', '/');
0726: if (!normalized.startsWith("/"))
0727: normalized = "/" + normalized;
0728:
0729: // Resolve occurrences of "//" in the normalized path
0730: while (true) {
0731: int index = normalized.indexOf("//");
0732: if (index < 0)
0733: break;
0734: normalized = normalized.substring(0, index)
0735: + normalized.substring(index + 1);
0736: }
0737:
0738: // Resolve occurrences of "/./" in the normalized path
0739: while (true) {
0740: int index = normalized.indexOf("/./");
0741: if (index < 0)
0742: break;
0743: normalized = normalized.substring(0, index)
0744: + normalized.substring(index + 2);
0745: }
0746:
0747: // Resolve occurrences of "/../" in the normalized path
0748: while (true) {
0749: int index = normalized.indexOf("/../");
0750: if (index < 0)
0751: break;
0752: if (index == 0)
0753: return (null); // Trying to go outside our context
0754: int index2 = normalized.lastIndexOf('/', index - 1);
0755: normalized = normalized.substring(0, index2)
0756: + normalized.substring(index + 3);
0757: }
0758:
0759: // Return the normalized path that we have completed
0760: return (normalized);
0761:
0762: }
0763:
0764: /**
0765: * Return a File object representing the specified normalized
0766: * context-relative path if it exists and is readable. Otherwise,
0767: * return <code>null</code>.
0768: *
0769: * @param name Normalized context-relative path (with leading '/')
0770: */
0771: protected File file(String name) {
0772:
0773: File file = new File(base, name);
0774: if (file.exists() && file.canRead()) {
0775:
0776: // Check that this file belongs to our root path
0777: String canPath = null;
0778: try {
0779: canPath = file.getCanonicalPath();
0780: } catch (IOException e) {
0781: }
0782: if (canPath == null)
0783: return null;
0784:
0785: // Check to see if going outside of the web application root
0786: if ((!allowLinking) && (!canPath.startsWith(absoluteBase))) {
0787: return null;
0788: }
0789:
0790: // Case sensitivity check
0791: if (!allowLinking && caseSensitive) {
0792: String fileAbsPath = file.getAbsolutePath();
0793: if (fileAbsPath.endsWith("."))
0794: fileAbsPath = fileAbsPath + "/";
0795: String absPath = normalize(fileAbsPath);
0796: if (canPath != null)
0797: canPath = normalize(canPath);
0798: if ((absoluteBase.length() < absPath.length())
0799: && (absoluteBase.length() < canPath.length())) {
0800: absPath = absPath
0801: .substring(absoluteBase.length() + 1);
0802: if ((canPath == null) || (absPath == null))
0803: return null;
0804: if (absPath.equals(""))
0805: absPath = "/";
0806: canPath = canPath
0807: .substring(absoluteBase.length() + 1);
0808: if (canPath.equals(""))
0809: canPath = "/";
0810: if (!canPath.equals(absPath))
0811: return null;
0812: }
0813: }
0814:
0815: } else {
0816: return null;
0817: }
0818: return file;
0819:
0820: }
0821:
0822: /**
0823: * List the resources which are members of a collection.
0824: *
0825: * @param file Collection
0826: * @return Vector containg NamingEntry objects
0827: */
0828: protected Vector list(File file) {
0829:
0830: Vector entries = new Vector();
0831: if (!file.isDirectory())
0832: return entries;
0833: String[] names = file.list();
0834: Arrays.sort(names); // Sort alphabetically
0835: if (names == null)
0836: return entries;
0837: NamingEntry entry = null;
0838:
0839: for (int i = 0; i < names.length; i++) {
0840:
0841: File currentFile = new File(file, names[i]);
0842: Object object = null;
0843: if (currentFile.isDirectory()) {
0844: FileDirContext tempContext = new FileDirContext(env);
0845: tempContext.setDocBase(file.getPath());
0846: object = tempContext;
0847: } else {
0848: object = new FileResource(currentFile);
0849: }
0850: entry = new NamingEntry(names[i], object, NamingEntry.ENTRY);
0851: entries.addElement(entry);
0852:
0853: }
0854:
0855: return entries;
0856:
0857: }
0858:
0859: // ----------------------------------------------- FileResource Inner Class
0860:
0861: /**
0862: * This specialized resource implementation avoids opening the IputStream
0863: * to the file right away (which would put a lock on the file).
0864: */
0865: protected class FileResource extends Resource {
0866:
0867: // -------------------------------------------------------- Constructor
0868:
0869: public FileResource(File file) {
0870: this .file = file;
0871: }
0872:
0873: // --------------------------------------------------- Member Variables
0874:
0875: /**
0876: * Associated file object.
0877: */
0878: protected File file;
0879:
0880: /**
0881: * File length.
0882: */
0883: protected long length = -1L;
0884:
0885: // --------------------------------------------------- Resource Methods
0886:
0887: /**
0888: * Content accessor.
0889: *
0890: * @return InputStream
0891: */
0892: public InputStream streamContent() throws IOException {
0893: if (binaryContent == null) {
0894: inputStream = new FileInputStream(file);
0895: }
0896: return super .streamContent();
0897: }
0898:
0899: }
0900:
0901: // ------------------------------------- FileResourceAttributes Inner Class
0902:
0903: /**
0904: * This specialized resource attribute implementation does some lazy
0905: * reading (to speed up simple checks, like checking the last modified
0906: * date).
0907: */
0908: protected class FileResourceAttributes extends ResourceAttributes {
0909:
0910: // -------------------------------------------------------- Constructor
0911:
0912: public FileResourceAttributes(File file) {
0913: this .file = file;
0914: }
0915:
0916: // --------------------------------------------------- Member Variables
0917:
0918: protected File file;
0919:
0920: protected boolean accessed = false;
0921:
0922: // ----------------------------------------- ResourceAttributes Methods
0923:
0924: /**
0925: * Is collection.
0926: */
0927: public boolean isCollection() {
0928: if (!accessed) {
0929: collection = file.isDirectory();
0930: accessed = true;
0931: }
0932: return super .isCollection();
0933: }
0934:
0935: /**
0936: * Get content length.
0937: *
0938: * @return content length value
0939: */
0940: public long getContentLength() {
0941: if (contentLength != -1L)
0942: return contentLength;
0943: contentLength = file.length();
0944: return contentLength;
0945: }
0946:
0947: /**
0948: * Get creation time.
0949: *
0950: * @return creation time value
0951: */
0952: public long getCreation() {
0953: if (creation != -1L)
0954: return creation;
0955: creation = file.lastModified();
0956: return creation;
0957: }
0958:
0959: /**
0960: * Get creation date.
0961: *
0962: * @return Creation date value
0963: */
0964: public Date getCreationDate() {
0965: if (creation == -1L) {
0966: creation = file.lastModified();
0967: }
0968: return super .getCreationDate();
0969: }
0970:
0971: /**
0972: * Get last modified time.
0973: *
0974: * @return lastModified time value
0975: */
0976: public long getLastModified() {
0977: if (lastModified != -1L)
0978: return lastModified;
0979: lastModified = file.lastModified();
0980: return lastModified;
0981: }
0982:
0983: /**
0984: * Get lastModified date.
0985: *
0986: * @return LastModified date value
0987: */
0988: public Date getLastModifiedDate() {
0989: if (lastModified == -1L) {
0990: lastModified = file.lastModified();
0991: }
0992: return super .getLastModifiedDate();
0993: }
0994:
0995: /**
0996: * Get name.
0997: *
0998: * @return Name value
0999: */
1000: public String getName() {
1001: if (name == null)
1002: name = file.getName();
1003: return name;
1004: }
1005:
1006: /**
1007: * Get resource type.
1008: *
1009: * @return String resource type
1010: */
1011: public String getResourceType() {
1012: if (!accessed) {
1013: collection = file.isDirectory();
1014: accessed = true;
1015: }
1016: return super.getResourceType();
1017: }
1018:
1019: }
1020:
1021: }
|