KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > naming > modules > fs > FileDirContext


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.naming.modules.fs;
18
19 import java.io.File JavaDoc;
20 import java.io.FileOutputStream JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.io.InputStream JavaDoc;
23 import java.util.Arrays JavaDoc;
24 import java.util.Hashtable JavaDoc;
25 import java.util.Vector JavaDoc;
26
27 import javax.naming.Name JavaDoc;
28 import javax.naming.NameAlreadyBoundException JavaDoc;
29 import javax.naming.NamingEnumeration JavaDoc;
30 import javax.naming.NamingException JavaDoc;
31 import javax.naming.directory.Attributes JavaDoc;
32 import javax.naming.directory.DirContext JavaDoc;
33
34 import org.apache.naming.core.BaseDirContext;
35 import org.apache.naming.core.NamingContextEnumeration;
36 import org.apache.naming.core.NamingEntry;
37 import org.apache.tomcat.util.res.StringManager;
38
39 /**
40  * DirContext for a filesystem directory.
41  *
42  * The 'bind' operation will accept an InputStream ( TODO: File, any
43  * resource with content )
44  * and create the file. ( TODO: what attributes can we support ? )
45  *
46  * The lookup operation will return a FileDirContext or a File.
47  *
48  * Supported attributes: (TODO: lastModified, size, ...)
49  *
50  * Note that JNDI allows memory-efficient style, without having one wrapper
51  * object for each real resource.
52  *
53  * @author Remy Maucherat
54  * @author Costin Manolache
55  */

56 public class FileDirContext extends BaseDirContext {
57     
58     private static org.apache.commons.logging.Log log=
59         org.apache.commons.logging.LogFactory.getLog( FileDirContext.class );
60
61     // -------------------------------------------------------------- Constants
62

63     protected StringManager sm =
64         StringManager.getManager("org.apache.naming.res");
65
66     protected static final int BUFFER_SIZE = 2048;
67
68     // ----------------------------------------------------------- Constructors
69

70
71     /**
72      * Builds a file directory context using the given environment.
73      */

74     public FileDirContext() {
75         super();
76     }
77
78
79     /**
80      * Builds a file directory context using the given environment.
81      */

82     public FileDirContext(Hashtable JavaDoc env) {
83         super(env);
84     }
85
86
87     // ----------------------------------------------------- Instance Variables
88

89
90     /**
91      * The document base directory.
92      */

93     protected File JavaDoc base = null;
94
95
96     /**
97      * Absolute normalized filename of the base.
98      */

99     protected String JavaDoc absoluteBase = null;
100
101
102     /**
103      * Case sensitivity.
104      */

105     protected boolean caseSensitive = true;
106
107
108     /**
109      * The document base path.
110      */

111     protected String JavaDoc docBase = null;
112
113     // ------------------------------------------------------------- Properties
114

115
116     /**
117      * Set the document root.
118      *
119      * @param docBase The new document root
120      *
121      * @exception IllegalArgumentException if the specified value is not
122      * supported by this implementation
123      * @exception IllegalArgumentException if this would create a
124      * malformed URL
125      */

126     public void setDocBase(String JavaDoc docBase) {
127
128     // Validate the format of the proposed document root
129
if (docBase == null)
130         throw new IllegalArgumentException JavaDoc
131         (sm.getString("resources.null"));
132
133     // Calculate a File object referencing this document base directory
134
base = new File JavaDoc(docBase);
135         try {
136             base = base.getCanonicalFile();
137         } catch (IOException JavaDoc e) {
138             // Ignore
139
}
140
141     // Validate that the document base is an existing directory
142
if (!base.exists() || !base.isDirectory() || !base.canRead())
143         throw new IllegalArgumentException JavaDoc
144         (sm.getString("fileResources.base", docBase));
145         this.absoluteBase = base.getAbsolutePath();
146
147     // Change the document root property
148
this.docBase = docBase;
149
150     }
151
152     /**
153      * Return the document root for this component.
154      */

155     public String JavaDoc getDocBase() {
156     return (this.docBase);
157     }
158
159
160     /**
161      * Set case sensitivity.
162      */

163     public void setCaseSensitive(boolean caseSensitive) {
164         this.caseSensitive = caseSensitive;
165     }
166
167
168     /**
169      * Is case sensitive ?
170      */

171     public boolean isCaseSensitive() {
172         return caseSensitive;
173     }
174
175
176     // --------------------------------------------------------- Public Methods
177

178
179     /**
180      * Release any resources allocated for this directory context.
181      */

182     public void release() {
183         caseSensitive = true;
184         absoluteBase = null;
185         base = null;
186         super.release();
187     }
188
189     public void setAttribute( String JavaDoc name, Object JavaDoc v ) {
190         new Throwable JavaDoc().printStackTrace();
191         if (log.isDebugEnabled())
192             log.debug(name + " " + v );
193     }
194
195     // -------------------- BaseDirContext implementation --------------------
196

197     /**
198      * Retrieves the named object. The result is a File relative to the docBase
199      * or a FileDirContext for directories.
200      *
201      * @param name the name of the object to look up
202      * @return the object bound to name
203      * @exception NamingException if a naming exception is encountered
204      */

205     public Object JavaDoc lookup(Name JavaDoc nameObj, boolean resolveLinkx)
206         throws NamingException JavaDoc
207     {
208         if( log.isDebugEnabled() ) {
209             log.debug( "lookup " + nameObj );
210             log.debug( nameObj.get(0));
211         }
212         if( "fs:".equals( nameObj.get(0).toString() ))
213             nameObj=nameObj.getSuffix(1);
214         
215         String JavaDoc name=nameObj.toString(); // we need to convert anyway, for File constructor
216

217         Object JavaDoc result = null;
218         File JavaDoc file = file(name);
219         
220         if (file == null)
221             throw new NamingException JavaDoc
222                 (sm.getString("resources.notFound", name));
223         
224         if (file.isDirectory()) {
225             FileDirContext tempContext = new FileDirContext(env);
226             tempContext.setDocBase(file.getPath());
227             result = tempContext;
228         } else {
229             // TODO: based on the name, return various 'styles' of
230
// content
231
// TODO: use lazy streams, cacheable
232
result = file; //new FileResource(file);
233
}
234         
235         return result;
236     }
237
238
239     /**
240      * Unbinds the named object. Removes the terminal atomic name in name
241      * from the target context--that named by all but the terminal atomic
242      * part of name.
243      * <p>
244      * This method is idempotent. It succeeds even if the terminal atomic
245      * name is not bound in the target context, but throws
246      * NameNotFoundException if any of the intermediate contexts do not exist.
247      *
248      * @param name the name to bind; may not be empty
249      * @exception NameNotFoundException if an intermediate context does not
250      * exist
251      * @exception NamingException if a naming exception is encountered
252      */

253     public void unbind(Name JavaDoc nameObj)
254         throws NamingException JavaDoc
255     {
256         if( "fs:".equals( nameObj.get(0).toString() ))
257             nameObj=nameObj.getSuffix(1);
258         String JavaDoc name=nameObj.toString();
259         if( log.isDebugEnabled() ) log.debug( "unbind " + name );
260         File JavaDoc file = file(name);
261
262         if (file == null)
263             throw new NamingException JavaDoc
264                 (sm.getString("resources.notFound", name));
265
266         if (!file.delete())
267             throw new NamingException JavaDoc
268                 (sm.getString("resources.unbindFailed", name));
269
270     }
271
272
273     /**
274      * Binds a new name to the object bound to an old name, and unbinds the
275      * old name. Both names are relative to this context. Any attributes
276      * associated with the old name become associated with the new name.
277      * Intermediate contexts of the old name are not changed.
278      *
279      * @param oldName the name of the existing binding; may not be empty
280      * @param newName the name of the new binding; may not be empty
281      * @exception NameAlreadyBoundException if newName is already bound
282      * @exception NamingException if a naming exception is encountered
283      */

284     public void rename(Name JavaDoc oldNameO, Name JavaDoc newNameO)
285         throws NamingException JavaDoc
286     {
287         String JavaDoc oldName=oldNameO.toString();
288         String JavaDoc newName=newNameO.toString();
289         File JavaDoc file = file(oldName);
290
291         if (file == null)
292             throw new NamingException JavaDoc
293                 (sm.getString("resources.notFound", oldName));
294
295         File JavaDoc newFile = new File JavaDoc(base, newName);
296         
297         file.renameTo(newFile);
298     }
299
300
301     /**
302      * Enumerates the names bound in the named context, along with the class
303      * names of objects bound to them. The contents of any subcontexts are
304      * not included.
305      * <p>
306      * If a binding is added to or removed from this context, its effect on
307      * an enumeration previously returned is undefined.
308      *
309      * @param name the name of the context to list
310      * @return an enumeration of the names and class names of the bindings in
311      * this context. Each element of the enumeration is of type NameClassPair.
312      * @exception NamingException if a naming exception is encountered
313      */

314     public NamingEnumeration JavaDoc list(Name JavaDoc nameN)
315         throws NamingException JavaDoc
316     {
317         String JavaDoc name=nameN.toString();
318         if( log.isDebugEnabled() ) log.debug( "list " + name );
319         File JavaDoc file = file(name);
320
321         if (file == null)
322             throw new NamingException JavaDoc
323                 (sm.getString("resources.notFound", name));
324
325         Vector JavaDoc entries = list(file);
326
327         return new NamingContextEnumeration(entries.elements(), this, false);
328
329     }
330
331
332     /**
333      * Enumerates the names bound in the named context, along with the
334      * objects bound to them. The contents of any subcontexts are not
335      * included.
336      * <p>
337      * If a binding is added to or removed from this context, its effect on
338      * an enumeration previously returned is undefined.
339      *
340      * @param name the name of the context to list
341      * @return an enumeration of the bindings in this context.
342      * Each element of the enumeration is of type Binding.
343      * @exception NamingException if a naming exception is encountered
344      */

345     public NamingEnumeration JavaDoc listBindings(Name JavaDoc nameN)
346         throws NamingException JavaDoc
347     {
348         String JavaDoc name=nameN.toString();
349         if( log.isDebugEnabled() ) log.debug( "listBindings " + name );
350
351         File JavaDoc file = file(name);
352
353         if (file == null)
354             throw new NamingException JavaDoc
355                 (sm.getString("resources.notFound", name));
356
357         Vector JavaDoc entries = list(file);
358
359         return new NamingContextEnumeration(entries.elements(), this, true);
360
361     }
362
363
364     /**
365      * Destroys the named context and removes it from the namespace. Any
366      * attributes associated with the name are also removed. Intermediate
367      * contexts are not destroyed.
368      * <p>
369      * This method is idempotent. It succeeds even if the terminal atomic
370      * name is not bound in the target context, but throws
371      * NameNotFoundException if any of the intermediate contexts do not exist.
372      *
373      * In a federated naming system, a context from one naming system may be
374      * bound to a name in another. One can subsequently look up and perform
375      * operations on the foreign context using a composite name. However, an
376      * attempt destroy the context using this composite name will fail with
377      * NotContextException, because the foreign context is not a "subcontext"
378      * of the context in which it is bound. Instead, use unbind() to remove
379      * the binding of the foreign context. Destroying the foreign context
380      * requires that the destroySubcontext() be performed on a context from
381      * the foreign context's "native" naming system.
382      *
383      * @param name the name of the context to be destroyed; may not be empty
384      * @exception NameNotFoundException if an intermediate context does not
385      * exist
386      * @exception NotContextException if the name is bound but does not name
387      * a context, or does not name a context of the appropriate type
388      */

389     public void destroySubcontext(Name JavaDoc name)
390         throws NamingException JavaDoc
391     {
392         unbind(name);
393     }
394
395
396     /**
397      * Retrieves the full name of this context within its own namespace.
398      * <p>
399      * Many naming services have a notion of a "full name" for objects in
400      * their respective namespaces. For example, an LDAP entry has a
401      * distinguished name, and a DNS record has a fully qualified name. This
402      * method allows the client application to retrieve this name. The string
403      * returned by this method is not a JNDI composite name and should not be
404      * passed directly to context methods. In naming systems for which the
405      * notion of full name does not make sense,
406      * OperationNotSupportedException is thrown.
407      *
408      * @return this context's name in its own namespace; never null
409      * @exception OperationNotSupportedException if the naming system does
410      * not have the notion of a full name
411      * @exception NamingException if a naming exception is encountered
412      */

413     public String JavaDoc getNameInNamespace()
414         throws NamingException JavaDoc {
415         return docBase;
416     }
417
418
419     // ----------------------------------------------------- DirContext Methods
420

421
422     /**
423      * Retrieves selected attributes associated with a named object.
424      * See the class description regarding attribute models, attribute type
425      * names, and operational attributes.
426      *
427      * @return the requested attributes; never null
428      * @param name the name of the object from which to retrieve attributes
429      * @param attrIds the identifiers of the attributes to retrieve. null
430      * indicates that all attributes should be retrieved; an empty array
431      * indicates that none should be retrieved
432      * @exception NamingException if a naming exception is encountered
433      */

434     public Attributes JavaDoc getAttributes(Name JavaDoc nameN, String JavaDoc[] attrIds)
435         throws NamingException JavaDoc
436     {
437         String JavaDoc name=nameN.toString();
438         if( log.isDebugEnabled() ) log.debug( "getAttributes " + name );
439
440         // Building attribute list
441
File JavaDoc file = file(name);
442
443         if (file == null)
444             throw new NamingException JavaDoc
445                 (sm.getString("resources.notFound", name));
446
447         return new FileAttributes(file);
448
449     }
450
451     /**
452      * Binds a name to an object, along with associated attributes. If attrs
453      * is null, the resulting binding will have the attributes associated
454      * with obj if obj is a DirContext, and no attributes otherwise. If attrs
455      * is non-null, the resulting binding will have attrs as its attributes;
456      * any attributes associated with obj are ignored.
457      *
458      * @param name the name to bind; may not be empty
459      * @param obj the object to bind; possibly null
460      * @param attrs the attributes to associate with the binding
461      * @exception NameAlreadyBoundException if name is already bound
462      * @exception InvalidAttributesException if some "mandatory" attributes
463      * of the binding are not supplied
464      * @exception NamingException if a naming exception is encountered
465      */

466     public void bind(Name JavaDoc nameN, Object JavaDoc obj, Attributes JavaDoc attrs)
467         throws NamingException JavaDoc {
468
469         String JavaDoc name=nameN.toString();
470         // Note: No custom attributes allowed
471

472         File JavaDoc file = new File JavaDoc(base, name);
473         if (file.exists())
474             throw new NameAlreadyBoundException JavaDoc
475                 (sm.getString("resources.alreadyBound", name));
476         
477         rebind(name, obj, attrs);
478     }
479
480
481     /**
482      * Binds a name to an object, along with associated attributes,
483      * overwriting any existing binding. If attrs is null and obj is a
484      * DirContext, the attributes from obj are used. If attrs is null and obj
485      * is not a DirContext, any existing attributes associated with the object
486      * already bound in the directory remain unchanged. If attrs is non-null,
487      * any existing attributes associated with the object already bound in
488      * the directory are removed and attrs is associated with the named
489      * object. If obj is a DirContext and attrs is non-null, the attributes
490      * of obj are ignored.
491      *
492      * @param name the name to bind; may not be empty
493      * @param obj the object to bind; possibly null
494      * @param attrs the attributes to associate with the binding
495      * @exception InvalidAttributesException if some "mandatory" attributes
496      * of the binding are not supplied
497      * @exception NamingException if a naming exception is encountered
498      */

499     public void rebind(Name JavaDoc nameN, Object JavaDoc obj, Attributes JavaDoc attrs)
500         throws NamingException JavaDoc {
501         String JavaDoc name=nameN.toString();
502
503         // Note: No custom attributes allowed
504
// Check obj type
505

506         File JavaDoc file = new File JavaDoc(base, name);
507
508         InputStream JavaDoc is = null;
509 // if (obj instanceof Resource) {
510
// try {
511
// is = ((Resource) obj).streamContent();
512
// } catch (IOException e) {
513
// }
514
// } else
515

516         // TODO support File, byte[], String
517
if (obj instanceof InputStream JavaDoc) {
518             is = (InputStream JavaDoc) obj;
519         } else if (obj instanceof DirContext JavaDoc) {
520             if (file.exists()) {
521                 if (!file.delete())
522                     throw new NamingException JavaDoc
523                         (sm.getString("resources.bindFailed", name));
524             }
525             if (!file.mkdir())
526                 throw new NamingException JavaDoc
527                     (sm.getString("resources.bindFailed", name));
528         }
529         if (is == null)
530             throw new NamingException JavaDoc
531                 (sm.getString("resources.bindFailed", name));
532
533         // Open os
534

535         try {
536             FileOutputStream JavaDoc os = null;
537             byte buffer[] = new byte[BUFFER_SIZE];
538             int len = -1;
539             try {
540                 os = new FileOutputStream JavaDoc(file);
541                 while (true) {
542                     len = is.read(buffer);
543                     if (len == -1)
544                         break;
545                     os.write(buffer, 0, len);
546                 }
547             } finally {
548                 if (os != null)
549                     os.close();
550                 is.close();
551             }
552         } catch (IOException JavaDoc e) {
553             throw new NamingException JavaDoc
554                 (sm.getString("resources.bindFailed", e));
555         }
556     }
557
558
559     /**
560      * Creates and binds a new context, along with associated attributes.
561      * This method creates a new subcontext with the given name, binds it in
562      * the target context (that named by all but terminal atomic component of
563      * the name), and associates the supplied attributes with the newly
564      * created object. All intermediate and target contexts must already
565      * exist. If attrs is null, this method is equivalent to
566      * Context.createSubcontext().
567      *
568      * @param name the name of the context to create; may not be empty
569      * @param attrs the attributes to associate with the newly created context
570      * @return the newly created context
571      * @exception NameAlreadyBoundException if the name is already bound
572      * @exception InvalidAttributesException if attrs does not contain all
573      * the mandatory attributes required for creation
574      * @exception NamingException if a naming exception is encountered
575      */

576     public DirContext JavaDoc createSubcontext(Name JavaDoc nameN, Attributes JavaDoc attrs)
577         throws NamingException JavaDoc
578     {
579         String JavaDoc name=nameN.toString();
580         File JavaDoc file = new File JavaDoc(base, name);
581         if (file.exists())
582             throw new NameAlreadyBoundException JavaDoc
583                 (sm.getString("resources.alreadyBound", name));
584         if (!file.mkdir())
585             throw new NamingException JavaDoc
586                 (sm.getString("resources.bindFailed", name));
587         return (DirContext JavaDoc) lookup(name);
588     }
589
590     // ------------------------------------------------------ Protected Methods
591

592
593     /**
594      * Return a context-relative path, beginning with a "/", that represents
595      * the canonical version of the specified path after ".." and "." elements
596      * are resolved out. If the specified path attempts to go outside the
597      * boundaries of the current context (i.e. too many ".." path elements
598      * are present), return <code>null</code> instead.
599      *
600      * @param path Path to be normalized
601      */

602     protected String JavaDoc normalize(String JavaDoc path) {
603
604     String JavaDoc normalized = path;
605
606     // Normalize the slashes and add leading slash if necessary
607
if (normalized.indexOf('\\') >= 0)
608         normalized = normalized.replace('\\', '/');
609     if (!normalized.startsWith("/"))
610         normalized = "/" + normalized;
611
612     // Resolve occurrences of "//" in the normalized path
613
while (true) {
614         int index = normalized.indexOf("//");
615         if (index < 0)
616         break;
617         normalized = normalized.substring(0, index) +
618         normalized.substring(index + 1);
619     }
620
621     // Resolve occurrences of "/./" in the normalized path
622
while (true) {
623         int index = normalized.indexOf("/./");
624         if (index < 0)
625         break;
626         normalized = normalized.substring(0, index) +
627         normalized.substring(index + 2);
628     }
629
630     // Resolve occurrences of "/../" in the normalized path
631
while (true) {
632         int index = normalized.indexOf("/../");
633         if (index < 0)
634         break;
635         if (index == 0)
636         return (null); // Trying to go outside our context
637
int index2 = normalized.lastIndexOf('/', index - 1);
638         normalized = normalized.substring(0, index2) +
639         normalized.substring(index + 3);
640     }
641
642     // Return the normalized path that we have completed
643
return (normalized);
644
645     }
646
647
648     /**
649      * Return a File object representing the specified normalized
650      * context-relative path if it exists and is readable. Otherwise,
651      * return <code>null</code>.
652      *
653      * @param name Normalized context-relative path (with leading '/')
654      */

655     protected File JavaDoc file(String JavaDoc name) {
656
657         File JavaDoc file = new File JavaDoc(base, name);
658         if (file.exists() && file.canRead()) {
659
660             // Check that this file belongs to our root path
661
String JavaDoc canPath = null;
662             try {
663                 canPath = file.getCanonicalPath();
664             } catch (IOException JavaDoc e) {
665             }
666             if (canPath == null)
667                 return null;
668
669             if (!canPath.startsWith(absoluteBase)) {
670                 return null;
671             }
672
673             // Windows only check
674
if ((caseSensitive) && (File.separatorChar == '\\')) {
675                 String JavaDoc fileAbsPath = file.getAbsolutePath();
676                 if (fileAbsPath.endsWith("."))
677                     fileAbsPath = fileAbsPath + "/";
678                 String JavaDoc absPath = normalize(fileAbsPath);
679                 if (canPath != null)
680                     canPath = normalize(canPath);
681                 if ((absoluteBase.length() < absPath.length())
682                     && (absoluteBase.length() < canPath.length())) {
683                     absPath = absPath.substring(absoluteBase.length() + 1);
684                     if ((canPath == null) || (absPath == null))
685                         return null;
686                     if (absPath.equals(""))
687                         absPath = "/";
688                     canPath = canPath.substring(absoluteBase.length() + 1);
689                     if (canPath.equals(""))
690                         canPath = "/";
691                     if (!canPath.equals(absPath))
692                         return null;
693                 }
694             }
695
696         } else {
697             if( log.isDebugEnabled() ) log.debug( file + " " +
698                                                   file.exists() + " " +
699                                                   file.canRead() );
700             return null;
701         }
702         return file;
703
704     }
705
706
707     /**
708      * List the resources which are members of a collection.
709      *
710      * @param file Collection
711      * @return Vector containg NamingEntry objects
712      */

713     protected Vector JavaDoc list(File JavaDoc file) {
714
715         Vector JavaDoc entries = new Vector JavaDoc();
716         if (!file.isDirectory())
717             return entries;
718         String JavaDoc[] names = file.list();
719         Arrays.sort(names); // Sort alphabetically
720
if (names == null)
721             return entries;
722         NamingEntry entry = null;
723
724         for (int i = 0; i < names.length; i++) {
725
726             File JavaDoc currentFile = new File JavaDoc(file, names[i]);
727             Object JavaDoc object = null;
728             if (currentFile.isDirectory()) {
729                 FileDirContext tempContext = new FileDirContext(env);
730                 tempContext.setDocBase(file.getPath());
731                 object = tempContext;
732             } else {
733                 //object = new FileResource(currentFile);
734
object = currentFile;
735             }
736             entry = new NamingEntry(names[i], object, null, NamingEntry.ENTRY);
737             entries.addElement(entry);
738
739         }
740
741         return entries;
742
743     }
744
745 }
746
747
Popular Tags