KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > naming > internal > ResourceManager


1 /*
2  * @(#)ResourceManager.java 1.13 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package com.sun.naming.internal;
9
10 import java.applet.Applet JavaDoc;
11 import java.io.InputStream JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.net.URL JavaDoc;
14 import java.lang.ref.WeakReference JavaDoc;
15 import java.util.Enumeration JavaDoc;
16 import java.util.HashMap JavaDoc;
17 import java.util.Hashtable JavaDoc;
18 import java.util.Map JavaDoc;
19 import java.util.Properties JavaDoc;
20 import java.util.StringTokenizer JavaDoc;
21 import java.util.List JavaDoc;
22 import java.util.ArrayList JavaDoc;
23 import java.util.WeakHashMap JavaDoc;
24
25 import javax.naming.*;
26
27 /**
28   * The ResourceManager class facilitates the reading of JNDI resource files.
29   *
30   * @author Rosanna Lee
31   * @author Scott Seligman
32   * @version 1.13 03/12/19
33   */

34
35 public final class ResourceManager {
36
37     /*
38      * Name of provider resource files (without the package-name prefix.)
39      */

40     private static final String JavaDoc PROVIDER_RESOURCE_FILE_NAME =
41         "jndiprovider.properties";
42
43     /*
44      * Name of application resource files.
45      */

46     private static final String JavaDoc APP_RESOURCE_FILE_NAME = "jndi.properties";
47
48     /*
49      * Name of properties file in <java.home>/lib.
50      */

51     private static final String JavaDoc JRELIB_PROPERTY_FILE_NAME = "jndi.properties";
52
53     /*
54      * The standard JNDI properties that specify colon-separated lists.
55      */

56     private static final String JavaDoc[] listProperties = {
57     Context.OBJECT_FACTORIES,
58     Context.URL_PKG_PREFIXES,
59     Context.STATE_FACTORIES,
60     // The following shouldn't create a runtime dependence on ldap package.
61
javax.naming.ldap.LdapContext.CONTROL_FACTORIES
62     };
63
64     private static final VersionHelper helper =
65         VersionHelper.getVersionHelper();
66
67     /*
68      * A cache of the properties that have been constructed by
69      * the ResourceManager. A Hashtable from a provider resource
70      * file is keyed on a class in the resource file's package.
71      * One from application resource files is keyed on the thread's
72      * context class loader.
73      */

74     private static final WeakHashMap JavaDoc propertiesCache = new WeakHashMap JavaDoc(11);
75
76     /*
77      * A cache of factory objects (ObjectFactory, StateFactory, ControlFactory).
78      *
79      * A two-level cache keyed first on context class loader and then
80      * on propValue. Value is a list of class or factory objects,
81      * weakly referenced so as not to prevent GC of the class loader.
82      * Used in getFactories().
83      */

84     private static final WeakHashMap JavaDoc factoryCache = new WeakHashMap JavaDoc(11);
85
86     /*
87      * A cache of URL factory objects (ObjectFactory).
88      *
89      * A two-level cache keyed first on context class loader and then
90      * on classSuffix+propValue. Value is the factory itself (weakly
91      * referenced so as not to prevent GC of the class loader) or
92      * NO_FACTORY if a previous search revealed no factory. Used in
93      * getFactory().
94      */

95     private static final WeakHashMap JavaDoc urlFactoryCache = new WeakHashMap JavaDoc(11);
96     private static final WeakReference JavaDoc NO_FACTORY = new WeakReference JavaDoc(null);
97
98
99     // There should be no instances of this class.
100
private ResourceManager() {
101     }
102
103
104     // ---------- Public methods ----------
105

106     /*
107      * Given the environment parameter passed to the initial context
108      * constructor, returns the full environment for that initial
109      * context (never null). This is based on the environment
110      * parameter, the applet parameters (where appropriate), the
111      * system properties, and all application resource files.
112      *
113      * <p> This method will modify <tt>env</tt> and save
114      * a reference to it. The caller may no longer modify it.
115      *
116      * @param env environment passed to initial context constructor.
117      * Null indicates an empty environment.
118      *
119      * @throws NamingException if an error occurs while reading a
120      * resource file
121      */

122     public static Hashtable JavaDoc getInitialEnvironment(Hashtable JavaDoc env)
123         throws NamingException
124     {
125     String JavaDoc[] props = VersionHelper.PROPS; // system/applet properties
126
if (env == null) {
127         env = new Hashtable JavaDoc(11);
128     }
129     Applet JavaDoc applet = (Applet JavaDoc)env.get(Context.APPLET);
130
131     // Merge property values from env param, applet params, and system
132
// properties. The first value wins: there's no concatenation of
133
// colon-separated lists.
134
// Read system properties by first trying System.getProperties(),
135
// and then trying System.getProperty() if that fails. The former
136
// is more efficient due to fewer permission checks.
137
//
138
String JavaDoc[] jndiSysProps = helper.getJndiProperties();
139     for (int i = 0; i < props.length; i++) {
140         Object JavaDoc val = env.get(props[i]);
141         if (val == null) {
142         if (applet != null) {
143             val = applet.getParameter(props[i]);
144         }
145         if (val == null) {
146             // Read system property.
147
val = (jndiSysProps != null)
148             ? jndiSysProps[i]
149             : helper.getJndiProperty(i);
150         }
151         if (val != null) {
152             env.put(props[i], val);
153         }
154         }
155     }
156
157     // Merge the above with the values read from all application
158
// resource files. Colon-separated lists are concatenated.
159
mergeTables(env, getApplicationResources());
160     return env;
161     }
162
163     /**
164       * Retrieves the property from the environment, or from the provider
165       * resource file associated with the given context. The environment
166       * may in turn contain values that come from applet parameters,
167       * system properties, or application resource files.
168       *
169       * If <tt>concat</tt> is true and both the environment and the provider
170       * resource file contain the property, the two values are concatenated
171       * (with a ':' separator).
172       *
173       * Returns null if no value is found.
174       *
175       * @param propName The non-null property name
176       * @param env The possibly null environment properties
177       * @param ctx The possibly null context
178       * @param concat True if multiple values should be concatenated
179       * @return the property value, or null is there is none.
180       * @throws NamingException if an error occurs while reading the provider
181       * resource file.
182       */

183     public static String JavaDoc getProperty(String JavaDoc propName, Hashtable JavaDoc env,
184     Context ctx, boolean concat)
185         throws NamingException {
186
187     String JavaDoc val1 = (env != null) ? (String JavaDoc)env.get(propName) : null;
188     if ((ctx == null) ||
189         ((val1 != null) && !concat)) {
190         return val1;
191     }
192     String JavaDoc val2 = (String JavaDoc)getProviderResource(ctx).get(propName);
193     if (val1 == null) {
194         return val2;
195     } else if ((val2 == null) || !concat) {
196         return val1;
197     } else {
198         return (val1 + ":" + val2);
199     }
200     }
201
202     /**
203      * Retrieves an enumeration of factory classes/object specified by a
204      * property.
205      *
206      * The property is gotten from the environment and the provider
207      * resource file associated with the given context and concantenated.
208      * See getProperty(). The resulting property value is a list of class names.
209      *<p>
210      * This method then loads each class using the current thread's context
211      * class loader and keeps them in a list. Any class that cannot be loaded
212      * is ignored. The resulting list is then cached in a two-level
213      * hash table, keyed first by the context class loader and then by
214      * the property's value.
215      * The next time threads of the same context class loader call this
216      * method, they can use the cached list.
217      *<p>
218      * After obtaining the list either from the cache or by creating one from
219      * the property value, this method then creates and returns a
220      * FactoryEnumeration using the list. As the FactoryEnumeration is
221      * traversed, the cached Class object in the list is instantiated and
222      * replaced by an instance of the factory object itself. Both class
223      * objects and factories are wrapped in weak references so as not to
224      * prevent GC of the class loader.
225      *<p>
226      * Note that multiple threads can be accessing the same cached list
227      * via FactoryEnumeration, which locks the list during each next().
228      * The size of the list will not change,
229      * but a cached Class object might be replaced by an instantiated factory
230      * object.
231      *
232      * @param propName The non-null property name
233      * @param env The possibly null environment properties
234      * @param ctx The possibly null context
235      * @return An enumeration of factory classes/objects; null if none.
236      * @exception NamingException If encounter problem while reading the provider
237      * property file.
238      * @see javax.naming.spi.NamingManager#getObjectInstance
239      * @see javax.naming.spi.NamingManager#getStateToBind
240      * @see javax.naming.spi.DirectoryManager#getObjectInstance
241      * @see javax.naming.spi.DirectoryManager#getStateToBind
242      * @see javax.naming.ldap.ControlFactory#getControlInstance
243      */

244     public static FactoryEnumeration getFactories(String JavaDoc propName, Hashtable JavaDoc env,
245     Context ctx) throws NamingException {
246
247     String JavaDoc facProp = getProperty(propName, env, ctx, true);
248     if (facProp == null)
249         return null; // no classes specified; return null
250

251     // Cache is based on context class loader and property val
252
ClassLoader JavaDoc loader = helper.getContextClassLoader();
253
254     Map JavaDoc perLoaderCache = null;
255     synchronized (factoryCache) {
256         perLoaderCache = (Map JavaDoc) factoryCache.get(loader);
257         if (perLoaderCache == null) {
258         perLoaderCache = new HashMap JavaDoc(11);
259         factoryCache.put(loader, perLoaderCache);
260         }
261     }
262
263     synchronized (perLoaderCache) {
264         List JavaDoc factories = (List JavaDoc) perLoaderCache.get(facProp);
265         if (factories != null) {
266         // Cached list
267
return factories.size() == 0 ? null
268             : new FactoryEnumeration(factories, loader);
269         } else {
270         // Populate list with classes named in facProp; skipping
271
// those that we cannot load
272
StringTokenizer JavaDoc parser = new StringTokenizer JavaDoc(facProp, ":");
273         factories = new ArrayList JavaDoc(5);
274         while (parser.hasMoreTokens()) {
275             try {
276             // System.out.println("loading");
277
String JavaDoc className = parser.nextToken();
278             Class JavaDoc c = helper.loadClass(className, loader);
279             factories.add(new NamedWeakReference(c, className));
280             } catch (Exception JavaDoc e) {
281             // ignore ClassNotFoundException, IllegalArgumentException
282
}
283         }
284         // System.out.println("adding to cache: " + factories);
285
perLoaderCache.put(facProp, factories);
286         return new FactoryEnumeration(factories, loader);
287         }
288     }
289     }
290
291     /**
292      * Retrieves a factory from a list of packages specified in a
293      * property.
294      *
295      * The property is gotten from the environment and the provider
296      * resource file associated with the given context and concatenated.
297      * classSuffix is added to the end of this list.
298      * See getProperty(). The resulting property value is a list of package
299      * prefixes.
300      *<p>
301      * This method then constructs a list of class names by concatenating
302      * each package prefix with classSuffix and attempts to load and
303      * instantiate the class until one succeeds.
304      * Any class that cannot be loaded is ignored.
305      * The resulting object is then cached in a two-level hash table,
306      * keyed first by the context class loader and then by the property's
307      * value and classSuffix.
308      * The next time threads of the same context class loader call this
309      * method, they use the cached factory.
310      * If no factory can be loaded, NO_FACTORY is recorded in the table
311      * so that next time it'll return quickly.
312      *
313      * @param propName The non-null property name
314      * @param env The possibly null environment properties
315      * @param ctx The possibly null context
316      * @param classSuffix The non-null class name
317      * (e.g. ".ldap.ldapURLContextFactory).
318      * @param defaultPkgPrefix The non-null default package prefix.
319      * (e.g., "com.sun.jndi.url").
320      * @return An factory object; null if none.
321      * @exception NamingException If encounter problem while reading the provider
322      * property file, or problem instantiating the factory.
323      *
324      * @see javax.naming.spi.NamingManager#getURLContext
325      * @see javax.naming.spi.NamingManager#getURLObject
326      */

327     public static Object JavaDoc getFactory(String JavaDoc propName, Hashtable JavaDoc env, Context ctx,
328     String JavaDoc classSuffix, String JavaDoc defaultPkgPrefix) throws NamingException {
329
330     // Merge property with provider property and supplied default
331
String JavaDoc facProp = getProperty(propName, env, ctx, true);
332     if (facProp != null)
333         facProp += (":" + defaultPkgPrefix);
334     else
335         facProp = defaultPkgPrefix;
336
337     // Cache factory based on context class loader, class name, and
338
// property val
339
ClassLoader JavaDoc loader = helper.getContextClassLoader();
340     String JavaDoc key = classSuffix + " " + facProp;
341
342     Map JavaDoc perLoaderCache = null;
343     synchronized (urlFactoryCache) {
344         perLoaderCache = (Map JavaDoc) urlFactoryCache.get(loader);
345         if (perLoaderCache == null) {
346         perLoaderCache = new HashMap JavaDoc(11);
347         urlFactoryCache.put(loader, perLoaderCache);
348         }
349     }
350
351     synchronized (perLoaderCache) {
352         Object JavaDoc factory = null;
353
354         WeakReference JavaDoc factoryRef = (WeakReference JavaDoc) perLoaderCache.get(key);
355         if (factoryRef == NO_FACTORY) {
356         return null;
357         } else if (factoryRef != null) {
358         factory = factoryRef.get();
359         if (factory != null) { // check if weak ref has been cleared
360
return factory;
361         }
362         }
363
364         // Not cached; find first factory and cache
365
StringTokenizer JavaDoc parser = new StringTokenizer JavaDoc(facProp, ":");
366         String JavaDoc className;
367         while (factory == null && parser.hasMoreTokens()) {
368         className = parser.nextToken() + classSuffix;
369         try {
370             // System.out.println("loading " + className);
371
factory = helper.loadClass(className, loader).newInstance();
372         } catch (InstantiationException JavaDoc e) {
373             NamingException ne =
374             new NamingException("Cannot instantiate " + className);
375             ne.setRootCause(e);
376             throw ne;
377         } catch (IllegalAccessException JavaDoc e) {
378             NamingException ne =
379             new NamingException("Cannot access " + className);
380             ne.setRootCause(e);
381             throw ne;
382         } catch (Exception JavaDoc e) {
383             // ignore ClassNotFoundException, IllegalArgumentException,
384
// etc.
385
}
386         }
387
388         // Cache it.
389
perLoaderCache.put(key, (factory != null)
390                     ? new WeakReference JavaDoc(factory)
391                     : NO_FACTORY);
392         return factory;
393     }
394     }
395
396
397     // ---------- Private methods ----------
398

399     /*
400      * Returns the properties contained in the provider resource file
401      * of an object's package. Returns an empty hash table if the
402      * object is null or the resource file cannot be found. The
403      * results are cached.
404      *
405      * @throws NamingException if an error occurs while reading the file.
406      */

407     private static Hashtable JavaDoc getProviderResource(Object JavaDoc obj)
408         throws NamingException
409     {
410     if (obj == null) {
411         return (new Hashtable JavaDoc(1));
412     }
413     synchronized (propertiesCache) {
414         Class JavaDoc c = obj.getClass();
415
416         Hashtable JavaDoc props = (Hashtable JavaDoc)propertiesCache.get(c);
417         if (props != null) {
418         return props;
419         }
420         props = new Properties JavaDoc();
421
422         InputStream JavaDoc istream =
423         helper.getResourceAsStream(c, PROVIDER_RESOURCE_FILE_NAME);
424
425         if (istream != null) {
426         try {
427             ((Properties JavaDoc)props).load(istream);
428         } catch (IOException JavaDoc e) {
429             NamingException ne = new ConfigurationException(
430                 "Error reading provider resource file for " + c);
431             ne.setRootCause(e);
432             throw ne;
433         }
434         }
435         propertiesCache.put(c, props);
436         return props;
437     }
438     }
439
440
441     /*
442      * Returns the Hashtable (never null) that results from merging
443      * all application resource files available to this thread's
444      * context class loader. The properties file in <java.home>/lib
445      * is also merged in. The results are cached.
446      *
447      * SECURITY NOTES:
448      * 1. JNDI needs permission to read the application resource files.
449      * 2. Any class will be able to use JNDI to view the contents of
450      * the application resource files in its own classpath. Give
451      * careful consideration to this before storing sensitive
452      * information there.
453      *
454      * @throws NamingException if an error occurs while reading a resource
455      * file.
456      */

457     private static Hashtable JavaDoc getApplicationResources() throws NamingException {
458
459     ClassLoader JavaDoc cl = helper.getContextClassLoader();
460
461     synchronized (propertiesCache) {
462         Hashtable JavaDoc result = (Hashtable JavaDoc)propertiesCache.get(cl);
463         if (result != null) {
464         return result;
465         }
466
467         try {
468         NamingEnumeration resources =
469             helper.getResources(cl, APP_RESOURCE_FILE_NAME);
470         while (resources.hasMore()) {
471             Properties JavaDoc props = new Properties JavaDoc();
472             props.load((InputStream JavaDoc)resources.next());
473
474             if (result == null) {
475             result = props;
476             } else {
477             mergeTables(result, props);
478             }
479         }
480
481         // Merge in properties from file in <java.home>/lib.
482
InputStream JavaDoc istream =
483             helper.getJavaHomeLibStream(JRELIB_PROPERTY_FILE_NAME);
484         if (istream != null) {
485             Properties JavaDoc props = new Properties JavaDoc();
486             props.load(istream);
487
488             if (result == null) {
489             result = props;
490             } else {
491             mergeTables(result, props);
492             }
493         }
494         
495         } catch (IOException JavaDoc e) {
496         NamingException ne = new ConfigurationException(
497             "Error reading application resource file");
498         ne.setRootCause(e);
499         throw ne;
500         }
501         if (result == null) {
502         result = new Hashtable JavaDoc(11);
503         }
504         propertiesCache.put(cl, result);
505         return result;
506     }
507     }
508
509     /*
510      * Merge the properties from one hash table into another. Each
511      * property in props2 that is not in props1 is added to props1.
512      * For each property in both hash tables that is one of the
513      * standard JNDI properties that specify colon-separated lists,
514      * the values are concatenated and stored in props1.
515      */

516     private static void mergeTables(Hashtable JavaDoc props1, Hashtable JavaDoc props2) {
517     Enumeration JavaDoc keys = props2.keys();
518
519     while (keys.hasMoreElements()) {
520         String JavaDoc prop = (String JavaDoc)keys.nextElement();
521         Object JavaDoc val1 = props1.get(prop);
522         if (val1 == null) {
523         props1.put(prop, props2.get(prop));
524         } else if (isListProperty(prop)) {
525         String JavaDoc val2 = (String JavaDoc)props2.get(prop);
526         props1.put(prop, ((String JavaDoc)val1) + ":" + val2);
527         }
528     }
529     }
530
531     /*
532      * Is a property one of the standard JNDI properties that specify
533      * colon-separated lists?
534      */

535     private static boolean isListProperty(String JavaDoc prop) {
536     prop = prop.intern();
537     for (int i = 0; i < listProperties.length; i++) {
538         if (prop == listProperties[i]) {
539         return true;
540         }
541     }
542     return false;
543     }
544 }
545
Popular Tags