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