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

Save This Page
Home » openjdk-7 » com.sun.naming » internal » [javadoc | source]