History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/com/sun/naming/internal/ResourceManager.java (1 of 18352 files). Show all

Unified · Split

java/pkg/com/sun/naming/internal/ResourceManager.java+608−0
@@ -0,0 +1,608 @@
1/*
2 * Copyright (c) 1999, 2001, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.naming.internal;
27
28import java.io.InputStream;
29import java.io.IOException;
30import java.net.URL;
31import java.lang.ref.WeakReference;
32import java.lang.reflect.Method;
33import java.lang.reflect.InvocationTargetException;
34import java.util.Enumeration;
35import java.util.HashMap;
36import java.util.Hashtable;
37import java.util.Map;
38import java.util.Properties;
39import java.util.StringTokenizer;
40import java.util.List;
41import java.util.ArrayList;
42import java.util.WeakHashMap;
43
44import javax.naming.*;
45
46/**
47 * The ResourceManager class facilitates the reading of JNDI resource files.
48 *
49 * @author Rosanna Lee
50 * @author Scott Seligman
51 */
52
53public final class ResourceManager {
54
55 /*
56 * Name of provider resource files (without the package-name prefix.)
57 */
58 private static final String PROVIDER_RESOURCE_FILE_NAME =
59 "jndiprovider.properties";
60
61 /*
62 * Name of application resource files.
63 */
64 private static final String APP_RESOURCE_FILE_NAME = "jndi.properties";
65
66 /*
67 * Name of properties file in <java.home>/lib.
68 */
69 private static final String JRELIB_PROPERTY_FILE_NAME = "jndi.properties";
70
71 /*
72 * The standard JNDI properties that specify colon-separated lists.
73 */
74 private static final String[] listProperties = {
75 Context.OBJECT_FACTORIES,
76 Context.URL_PKG_PREFIXES,
77 Context.STATE_FACTORIES,
78 // The following shouldn't create a runtime dependence on ldap package.
79 javax.naming.ldap.LdapContext.CONTROL_FACTORIES
80 };
81
82 private static final VersionHelper helper =
83 VersionHelper.getVersionHelper();
84
85 /*
86 * A cache of the properties that have been constructed by
87 * the ResourceManager. A Hashtable from a provider resource
88 * file is keyed on a class in the resource file's package.
89 * One from application resource files is keyed on the thread's
90 * context class loader.
91 */
92 private static final WeakHashMap propertiesCache = new WeakHashMap(11);
93
94 /*
95 * A cache of factory objects (ObjectFactory, StateFactory, ControlFactory).
96 *
97 * A two-level cache keyed first on context class loader and then
98 * on propValue. Value is a list of class or factory objects,
99 * weakly referenced so as not to prevent GC of the class loader.
100 * Used in getFactories().
101 */
102 private static final WeakHashMap factoryCache = new WeakHashMap(11);
103
104 /*
105 * A cache of URL factory objects (ObjectFactory).
106 *
107 * A two-level cache keyed first on context class loader and then
108 * on classSuffix+propValue. Value is the factory itself (weakly
109 * referenced so as not to prevent GC of the class loader) or
110 * NO_FACTORY if a previous search revealed no factory. Used in
111 * getFactory().
112 */
113 private static final WeakHashMap urlFactoryCache = new WeakHashMap(11);
114 private static final WeakReference NO_FACTORY = new WeakReference(null);
115
116 /**
117 * A class to allow JNDI properties be specified as applet parameters
118 * without creating a static dependency on java.applet.
119 */
120 private static class AppletParameter {
121 private static final Class<?> clazz = getClass("java.applet.Applet");
122 private static final Method getMethod =
123 getMethod(clazz, "getParameter", String.class);
124 private static Class<?> getClass(String name) {
125 try {
126 return Class.forName(name, true, null);
127 } catch (ClassNotFoundException e) {
128 return null;
129 }
130 }
131 private static Method getMethod(Class<?> clazz,
132 String name,
133 Class<?>... paramTypes)
134 {
135 if (clazz != null) {
136 try {
137 return clazz.getMethod(name, paramTypes);
138 } catch (NoSuchMethodException e) {
139 throw new AssertionError(e);
140 }
141 } else {
142 return null;
143 }
144 }
145
146 /**
147 * Returns the value of the applet's named parameter.
148 */
149 static Object get(Object applet, String name) {
150 // if clazz is null then applet cannot be an Applet.
151 if (clazz == null || !clazz.isInstance(applet))
152 throw new ClassCastException(applet.getClass().getName());
153 try {
154 return getMethod.invoke(applet, name);
155 } catch (InvocationTargetException e) {
156 throw new AssertionError(e);
157 } catch (IllegalAccessException iae) {
158 throw new AssertionError(iae);
159 }
160 }
161 }
162
163 // There should be no instances of this class.
164 private ResourceManager() {
165 }
166
167
168 // ---------- Public methods ----------
169
170 /*
171 * Given the environment parameter passed to the initial context
172 * constructor, returns the full environment for that initial
173 * context (never null). This is based on the environment
174 * parameter, the applet parameters (where appropriate), the
175 * system properties, and all application resource files.
176 *
177 * <p> This method will modify <tt>env</tt> and save
178 * a reference to it. The caller may no longer modify it.
179 *
180 * @param env environment passed to initial context constructor.
181 * Null indicates an empty environment.
182 *
183 * @throws NamingException if an error occurs while reading a
184 * resource file
185 */
186 public static Hashtable getInitialEnvironment(Hashtable env)
187 throws NamingException
188 {
189 String[] props = VersionHelper.PROPS; // system/applet properties
190 if (env == null) {
191 env = new Hashtable(11);
192 }
193 Object applet = env.get(Context.APPLET);
194
195 // Merge property values from env param, applet params, and system
196 // properties. The first value wins: there's no concatenation of
197 // colon-separated lists.
198 // Read system properties by first trying System.getProperties(),
199 // and then trying System.getProperty() if that fails. The former
200 // is more efficient due to fewer permission checks.
201 //
202 String[] jndiSysProps = helper.getJndiProperties();
203 for (int i = 0; i < props.length; i++) {
204 Object val = env.get(props[i]);
205 if (val == null) {
206 if (applet != null) {
207 val = AppletParameter.get(applet, props[i]);
208 }
209 if (val == null) {
210 // Read system property.
211 val = (jndiSysProps != null)
212 ? jndiSysProps[i]
213 : helper.getJndiProperty(i);
214 }
215 if (val != null) {
216 env.put(props[i], val);
217 }
218 }
219 }
220
221 // Merge the above with the values read from all application
222 // resource files. Colon-separated lists are concatenated.
223 mergeTables(env, getApplicationResources());
224 return env;
225 }
226
227 /**
228 * Retrieves the property from the environment, or from the provider
229 * resource file associated with the given context. The environment
230 * may in turn contain values that come from applet parameters,
231 * system properties, or application resource files.
232 *
233 * If <tt>concat</tt> is true and both the environment and the provider
234 * resource file contain the property, the two values are concatenated
235 * (with a ':' separator).
236 *
237 * Returns null if no value is found.
238 *
239 * @param propName The non-null property name
240 * @param env The possibly null environment properties
241 * @param ctx The possibly null context
242 * @param concat True if multiple values should be concatenated
243 * @return the property value, or null is there is none.
244 * @throws NamingException if an error occurs while reading the provider
245 * resource file.
246 */
247 public static String getProperty(String propName, Hashtable env,
248 Context ctx, boolean concat)
249 throws NamingException {
250
251 String val1 = (env != null) ? (String)env.get(propName) : null;
252 if ((ctx == null) ||
253 ((val1 != null) && !concat)) {
254 return val1;
255 }
256 String val2 = (String)getProviderResource(ctx).get(propName);
257 if (val1 == null) {
258 return val2;
259 } else if ((val2 == null) || !concat) {
260 return val1;
261 } else {
262 return (val1 + ":" + val2);
263 }
264 }
265
266 /**
267 * Retrieves an enumeration of factory classes/object specified by a
268 * property.
269 *
270 * The property is gotten from the environment and the provider
271 * resource file associated with the given context and concantenated.
272 * See getProperty(). The resulting property value is a list of class names.
273 *<p>
274 * This method then loads each class using the current thread's context
275 * class loader and keeps them in a list. Any class that cannot be loaded
276 * is ignored. The resulting list is then cached in a two-level
277 * hash table, keyed first by the context class loader and then by
278 * the property's value.
279 * The next time threads of the same context class loader call this
280 * method, they can use the cached list.
281 *<p>
282 * After obtaining the list either from the cache or by creating one from
283 * the property value, this method then creates and returns a
284 * FactoryEnumeration using the list. As the FactoryEnumeration is
285 * traversed, the cached Class object in the list is instantiated and
286 * replaced by an instance of the factory object itself. Both class
287 * objects and factories are wrapped in weak references so as not to
288 * prevent GC of the class loader.
289 *<p>
290 * Note that multiple threads can be accessing the same cached list
291 * via FactoryEnumeration, which locks the list during each next().
292 * The size of the list will not change,
293 * but a cached Class object might be replaced by an instantiated factory
294 * object.
295 *
296 * @param propName The non-null property name
297 * @param env The possibly null environment properties
298 * @param ctx The possibly null context
299 * @return An enumeration of factory classes/objects; null if none.
300 * @exception NamingException If encounter problem while reading the provider
301 * property file.
302 * @see javax.naming.spi.NamingManager#getObjectInstance
303 * @see javax.naming.spi.NamingManager#getStateToBind
304 * @see javax.naming.spi.DirectoryManager#getObjectInstance
305 * @see javax.naming.spi.DirectoryManager#getStateToBind
306 * @see javax.naming.ldap.ControlFactory#getControlInstance
307 */
308 public static FactoryEnumeration getFactories(String propName, Hashtable env,
309 Context ctx) throws NamingException {
310
311 String facProp = getProperty(propName, env, ctx, true);
312 if (facProp == null)
313 return null; // no classes specified; return null
314
315 // Cache is based on context class loader and property val
316 ClassLoader loader = helper.getContextClassLoader();
317
318 Map perLoaderCache = null;
319 synchronized (factoryCache) {
320 perLoaderCache = (Map) factoryCache.get(loader);
321 if (perLoaderCache == null) {
322 perLoaderCache = new HashMap(11);
323 factoryCache.put(loader, perLoaderCache);
324 }
325 }
326
327 synchronized (perLoaderCache) {
328 List factories = (List) perLoaderCache.get(facProp);
329 if (factories != null) {
330 // Cached list
331 return factories.size() == 0 ? null
332 : new FactoryEnumeration(factories, loader);
333 } else {
334 // Populate list with classes named in facProp; skipping
335 // those that we cannot load
336 StringTokenizer parser = new StringTokenizer(facProp, ":");
337 factories = new ArrayList(5);
338 while (parser.hasMoreTokens()) {
339 try {
340 // System.out.println("loading");
341 String className = parser.nextToken();
342 Class c = helper.loadClass(className, loader);
343 factories.add(new NamedWeakReference(c, className));
344 } catch (Exception e) {
345 // ignore ClassNotFoundException, IllegalArgumentException
346 }
347 }
348 // System.out.println("adding to cache: " + factories);
349 perLoaderCache.put(facProp, factories);
350 return new FactoryEnumeration(factories, loader);
351 }
352 }
353 }
354
355 /**
356 * Retrieves a factory from a list of packages specified in a
357 * property.
358 *
359 * The property is gotten from the environment and the provider
360 * resource file associated with the given context and concatenated.
361 * classSuffix is added to the end of this list.
362 * See getProperty(). The resulting property value is a list of package
363 * prefixes.
364 *<p>
365 * This method then constructs a list of class names by concatenating
366 * each package prefix with classSuffix and attempts to load and
367 * instantiate the class until one succeeds.
368 * Any class that cannot be loaded is ignored.
369 * The resulting object is then cached in a two-level hash table,
370 * keyed first by the context class loader and then by the property's
371 * value and classSuffix.
372 * The next time threads of the same context class loader call this
373 * method, they use the cached factory.
374 * If no factory can be loaded, NO_FACTORY is recorded in the table
375 * so that next time it'll return quickly.
376 *
377 * @param propName The non-null property name
378 * @param env The possibly null environment properties
379 * @param ctx The possibly null context
380 * @param classSuffix The non-null class name
381 * (e.g. ".ldap.ldapURLContextFactory).
382 * @param defaultPkgPrefix The non-null default package prefix.
383 * (e.g., "com.sun.jndi.url").
384 * @return An factory object; null if none.
385 * @exception NamingException If encounter problem while reading the provider
386 * property file, or problem instantiating the factory.
387 *
388 * @see javax.naming.spi.NamingManager#getURLContext
389 * @see javax.naming.spi.NamingManager#getURLObject
390 */
391 public static Object getFactory(String propName, Hashtable env, Context ctx,
392 String classSuffix, String defaultPkgPrefix) throws NamingException {
393
394 // Merge property with provider property and supplied default
395 String facProp = getProperty(propName, env, ctx, true);
396 if (facProp != null)
397 facProp += (":" + defaultPkgPrefix);
398 else
399 facProp = defaultPkgPrefix;
400
401 // Cache factory based on context class loader, class name, and
402 // property val
403 ClassLoader loader = helper.getContextClassLoader();
404 String key = classSuffix + " " + facProp;
405
406 Map perLoaderCache = null;
407 synchronized (urlFactoryCache) {
408 perLoaderCache = (Map) urlFactoryCache.get(loader);
409 if (perLoaderCache == null) {
410 perLoaderCache = new HashMap(11);
411 urlFactoryCache.put(loader, perLoaderCache);
412 }
413 }
414
415 synchronized (perLoaderCache) {
416 Object factory = null;
417
418 WeakReference factoryRef = (WeakReference) perLoaderCache.get(key);
419 if (factoryRef == NO_FACTORY) {
420 return null;
421 } else if (factoryRef != null) {
422 factory = factoryRef.get();
423 if (factory != null) { // check if weak ref has been cleared
424 return factory;
425 }
426 }
427
428 // Not cached; find first factory and cache
429 StringTokenizer parser = new StringTokenizer(facProp, ":");
430 String className;
431 while (factory == null && parser.hasMoreTokens()) {
432 className = parser.nextToken() + classSuffix;
433 try {
434 // System.out.println("loading " + className);
435 factory = helper.loadClass(className, loader).newInstance();
436 } catch (InstantiationException e) {
437 NamingException ne =
438 new NamingException("Cannot instantiate " + className);
439 ne.setRootCause(e);
440 throw ne;
441 } catch (IllegalAccessException e) {
442 NamingException ne =
443 new NamingException("Cannot access " + className);
444 ne.setRootCause(e);
445 throw ne;
446 } catch (Exception e) {
447 // ignore ClassNotFoundException, IllegalArgumentException,
448 // etc.
449 }
450 }
451
452 // Cache it.
453 perLoaderCache.put(key, (factory != null)
454 ? new WeakReference(factory)
455 : NO_FACTORY);
456 return factory;
457 }
458 }
459
460
461 // ---------- Private methods ----------
462
463 /*
464 * Returns the properties contained in the provider resource file
465 * of an object's package. Returns an empty hash table if the
466 * object is null or the resource file cannot be found. The
467 * results are cached.
468 *
469 * @throws NamingException if an error occurs while reading the file.
470 */
471 private static Hashtable getProviderResource(Object obj)
472 throws NamingException
473 {
474 if (obj == null) {
475 return (new Hashtable(1));
476 }
477 synchronized (propertiesCache) {
478 Class c = obj.getClass();
479
480 Hashtable props = (Hashtable)propertiesCache.get(c);
481 if (props != null) {
482 return props;
483 }
484 props = new Properties();
485
486 InputStream istream =
487 helper.getResourceAsStream(c, PROVIDER_RESOURCE_FILE_NAME);
488
489 if (istream != null) {
490 try {
491 ((Properties)props).load(istream);
492 } catch (IOException e) {
493 NamingException ne = new ConfigurationException(
494 "Error reading provider resource file for " + c);
495 ne.setRootCause(e);
496 throw ne;
497 }
498 }
499 propertiesCache.put(c, props);
500 return props;
501 }
502 }
503
504
505 /*
506 * Returns the Hashtable (never null) that results from merging
507 * all application resource files available to this thread's
508 * context class loader. The properties file in <java.home>/lib
509 * is also merged in. The results are cached.
510 *
511 * SECURITY NOTES:
512 * 1. JNDI needs permission to read the application resource files.
513 * 2. Any class will be able to use JNDI to view the contents of
514 * the application resource files in its own classpath. Give
515 * careful consideration to this before storing sensitive
516 * information there.
517 *
518 * @throws NamingException if an error occurs while reading a resource
519 * file.
520 */
521 private static Hashtable getApplicationResources() throws NamingException {
522
523 ClassLoader cl = helper.getContextClassLoader();
524
525 synchronized (propertiesCache) {
526 Hashtable result = (Hashtable)propertiesCache.get(cl);
527 if (result != null) {
528 return result;
529 }
530
531 try {
532 NamingEnumeration resources =
533 helper.getResources(cl, APP_RESOURCE_FILE_NAME);
534 while (resources.hasMore()) {
535 Properties props = new Properties();
536 props.load((InputStream)resources.next());
537
538 if (result == null) {
539 result = props;
540 } else {
541 mergeTables(result, props);
542 }
543 }
544
545 // Merge in properties from file in <java.home>/lib.
546 InputStream istream =
547 helper.getJavaHomeLibStream(JRELIB_PROPERTY_FILE_NAME);
548 if (istream != null) {
549 Properties props = new Properties();
550 props.load(istream);
551
552 if (result == null) {
553 result = props;
554 } else {
555 mergeTables(result, props);
556 }
557 }
558
559 } catch (IOException e) {
560 NamingException ne = new ConfigurationException(
561 "Error reading application resource file");
562 ne.setRootCause(e);
563 throw ne;
564 }
565 if (result == null) {
566 result = new Hashtable(11);
567 }
568 propertiesCache.put(cl, result);
569 return result;
570 }
571 }
572
573 /*
574 * Merge the properties from one hash table into another. Each
575 * property in props2 that is not in props1 is added to props1.
576 * For each property in both hash tables that is one of the
577 * standard JNDI properties that specify colon-separated lists,
578 * the values are concatenated and stored in props1.
579 */
580 private static void mergeTables(Hashtable props1, Hashtable props2) {
581 Enumeration keys = props2.keys();
582
583 while (keys.hasMoreElements()) {
584 String prop = (String)keys.nextElement();
585 Object val1 = props1.get(prop);
586 if (val1 == null) {
587 props1.put(prop, props2.get(prop));
588 } else if (isListProperty(prop)) {
589 String val2 = (String)props2.get(prop);
590 props1.put(prop, ((String)val1) + ":" + val2);
591 }
592 }
593 }
594
595 /*
596 * Is a property one of the standard JNDI properties that specify
597 * colon-separated lists?
598 */
599 private static boolean isListProperty(String prop) {
600 prop = prop.intern();
601 for (int i = 0; i < listProperties.length; i++) {
602 if (prop == listProperties[i]) {
603 return true;
604 }
605 }
606 return false;
607 }
608}