1 /*
   2  * Copyright (c) 1999, 2012, 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 
  26 package javax.naming.spi;
  27 
  28 import java.util.Hashtable;
  29 
  30 import javax.naming.Context;
  31 import javax.naming.Name;
  32 import javax.naming.Reference;
  33 import javax.naming.Referenceable;
  34 import javax.naming.NamingException;
  35 import javax.naming.CannotProceedException;
  36 import javax.naming.directory.DirContext;
  37 import javax.naming.directory.Attributes;
  38 
  39 import com.sun.naming.internal.ResourceManager;
  40 import com.sun.naming.internal.FactoryEnumeration;
  41 
  42 
  43 /**
  44   * This class contains methods for supporting <tt>DirContext</tt>
  45   * implementations.
  46   *<p>
  47   * This class is an extension of <tt>NamingManager</tt>.  It contains methods
  48   * for use by service providers for accessing object factories and
  49   * state factories, and for getting continuation contexts for
  50   * supporting federation.
  51   *<p>
  52   * <tt>DirectoryManager</tt> is safe for concurrent access by multiple threads.
  53   *<p>
  54   * Except as otherwise noted,
  55   * a <tt>Name</tt>, <tt>Attributes</tt>, or environment parameter
  56   * passed to any method is owned by the caller.
  57   * The implementation will not modify the object or keep a reference
  58   * to it, although it may keep a reference to a clone or copy.
  59   *
  60   * @author Rosanna Lee
  61   * @author Scott Seligman
  62   *
  63   * @see DirObjectFactory
  64   * @see DirStateFactory
  65   * @since 1.3
  66   */
  67 
  68 public class DirectoryManager extends NamingManager {
  69 
  70     /*
  71      * Disallow anyone from creating one of these.
  72      */
  73     DirectoryManager() {}
  74 
  75     /**
  76       * Creates a context in which to continue a <tt>DirContext</tt> operation.
  77       * Operates just like <tt>NamingManager.getContinuationContext()</tt>,
  78       * only the continuation context returned is a <tt>DirContext</tt>.
  79       *
  80       * @param cpe
  81       *         The non-null exception that triggered this continuation.
  82       * @return A non-null <tt>DirContext</tt> object for continuing the operation.
  83       * @exception NamingException If a naming exception occurred.
  84       *
  85       * @see NamingManager#getContinuationContext(CannotProceedException)
  86       */
  87     @SuppressWarnings("unchecked")
  88     public static DirContext getContinuationDirContext(
  89             CannotProceedException cpe) throws NamingException {
  90 
  91         Hashtable<Object,Object> env = (Hashtable<Object,Object>)cpe.getEnvironment();
  92         if (env == null) {
  93             env = new Hashtable<>(7);
  94         } else {
  95             // Make a (shallow) copy of the environment.
  96             env = env.clone();
  97         }
  98         env.put(CPE, cpe);
  99 
 100         return (new ContinuationDirContext(cpe, env));
 101     }
 102 
 103     /**
 104       * Creates an instance of an object for the specified object,
 105       * attributes, and environment.
 106       * <p>
 107       * This method is the same as <tt>NamingManager.getObjectInstance</tt>
 108       * except for the following differences:
 109       *<ul>
 110       *<li>
 111       * It accepts an <tt>Attributes</tt> parameter that contains attributes
 112       * associated with the object. The <tt>DirObjectFactory</tt> might use these
 113       * attributes to save having to look them up from the directory.
 114       *<li>
 115       * The object factories tried must implement either
 116       * <tt>ObjectFactory</tt> or <tt>DirObjectFactory</tt>.
 117       * If it implements <tt>DirObjectFactory</tt>,
 118       * <tt>DirObjectFactory.getObjectInstance()</tt> is used, otherwise,
 119       * <tt>ObjectFactory.getObjectInstance()</tt> is used.
 120       *</ul>
 121       * Service providers that implement the <tt>DirContext</tt> interface
 122       * should use this method, not <tt>NamingManager.getObjectInstance()</tt>.
 123       *<p>
 124       *
 125       * @param refInfo The possibly null object for which to create an object.
 126       * @param name The name of this object relative to <code>nameCtx</code>.
 127       *         Specifying a name is optional; if it is
 128       *         omitted, <code>name</code> should be null.
 129       * @param nameCtx The context relative to which the <code>name</code>
 130       *         parameter is specified.  If null, <code>name</code> is
 131       *         relative to the default initial context.
 132       * @param environment The possibly null environment to
 133       *         be used in the creation of the object factory and the object.
 134       * @param attrs The possibly null attributes associated with refInfo.
 135       *         This might not be the complete set of attributes for refInfo;
 136       *         you might be able to read more attributes from the directory.
 137       * @return An object created using <code>refInfo</code> and <tt>attrs</tt>; or
 138       *         <code>refInfo</code> if an object cannot be created by
 139       *         a factory.
 140       * @exception NamingException If a naming exception was encountered
 141       *         while attempting to get a URL context, or if one of the
 142       *         factories accessed throws a NamingException.
 143       * @exception Exception If one of the factories accessed throws an
 144       *         exception, or if an error was encountered while loading
 145       *         and instantiating the factory and object classes.
 146       *         A factory should only throw an exception if it does not want
 147       *         other factories to be used in an attempt to create an object.
 148       *         See <tt>DirObjectFactory.getObjectInstance()</tt>.
 149       * @see NamingManager#getURLContext
 150       * @see DirObjectFactory
 151       * @see DirObjectFactory#getObjectInstance
 152       * @since 1.3
 153       */
 154     public static Object
 155         getObjectInstance(Object refInfo, Name name, Context nameCtx,
 156                           Hashtable<?,?> environment, Attributes attrs)
 157         throws Exception {
 158 
 159             ObjectFactory factory;
 160 
 161             ObjectFactoryBuilder builder = getObjectFactoryBuilder();
 162             if (builder != null) {
 163                 // builder must return non-null factory
 164                 factory = builder.createObjectFactory(refInfo, environment);
 165                 if (factory instanceof DirObjectFactory) {
 166                     return ((DirObjectFactory)factory).getObjectInstance(
 167                         refInfo, name, nameCtx, environment, attrs);
 168                 } else {
 169                     return factory.getObjectInstance(refInfo, name, nameCtx,
 170                         environment);
 171                 }
 172             }
 173 
 174             // use reference if possible
 175             Reference ref = null;
 176             if (refInfo instanceof Reference) {
 177                 ref = (Reference) refInfo;
 178             } else if (refInfo instanceof Referenceable) {
 179                 ref = ((Referenceable)(refInfo)).getReference();
 180             }
 181 
 182             Object answer;
 183 
 184             if (ref != null) {
 185                 String f = ref.getFactoryClassName();
 186                 if (f != null) {
 187                     // if reference identifies a factory, use exclusively
 188 
 189                     factory = getObjectFactoryFromReference(ref, f);
 190                     if (factory instanceof DirObjectFactory) {
 191                         return ((DirObjectFactory)factory).getObjectInstance(
 192                             ref, name, nameCtx, environment, attrs);
 193                     } else if (factory != null) {
 194                         return factory.getObjectInstance(ref, name, nameCtx,
 195                                                          environment);
 196                     }
 197                     // No factory found, so return original refInfo.
 198                     // Will reach this point if factory class is not in
 199                     // class path and reference does not contain a URL for it
 200                     return refInfo;
 201 
 202                 } else {
 203                     // if reference has no factory, check for addresses
 204                     // containing URLs
 205                     // ignore name & attrs params; not used in URL factory
 206 
 207                     answer = processURLAddrs(ref, name, nameCtx, environment);
 208                     if (answer != null) {
 209                         return answer;
 210                     }
 211                 }
 212             }
 213 
 214             // try using any specified factories
 215             answer = createObjectFromFactories(refInfo, name, nameCtx,
 216                                                environment, attrs);
 217             return (answer != null) ? answer : refInfo;
 218     }
 219 
 220     private static Object createObjectFromFactories(Object obj, Name name,
 221             Context nameCtx, Hashtable<?,?> environment, Attributes attrs)
 222         throws Exception {
 223 
 224         FactoryEnumeration factories = ResourceManager.getFactories(
 225             Context.OBJECT_FACTORIES, environment, nameCtx);
 226 
 227         if (factories == null)
 228             return null;
 229 
 230         ObjectFactory factory;
 231         Object answer = null;
 232         // Try each factory until one succeeds
 233         while (answer == null && factories.hasMore()) {
 234             factory = (ObjectFactory)factories.next();
 235             if (factory instanceof DirObjectFactory) {
 236                 answer = ((DirObjectFactory)factory).
 237                     getObjectInstance(obj, name, nameCtx, environment, attrs);
 238             } else {
 239                 answer =
 240                     factory.getObjectInstance(obj, name, nameCtx, environment);
 241             }
 242         }
 243         return answer;
 244     }
 245 
 246     /**
 247       * Retrieves the state of an object for binding when given the original
 248       * object and its attributes.
 249       * <p>
 250       * This method is like <tt>NamingManager.getStateToBind</tt> except
 251       * for the following differences:
 252       *<ul>
 253       *<li>It accepts an <tt>Attributes</tt> parameter containing attributes
 254       *    that were passed to the <tt>DirContext.bind()</tt> method.
 255       *<li>It returns a non-null <tt>DirStateFactory.Result</tt> instance
 256       *    containing the object to be bound, and the attributes to
 257       *    accompany the binding. Either the object or the attributes may be null.
 258       *<li>
 259       * The state factories tried must each implement either
 260       * <tt>StateFactory</tt> or <tt>DirStateFactory</tt>.
 261       * If it implements <tt>DirStateFactory</tt>, then
 262       * <tt>DirStateFactory.getStateToBind()</tt> is called; otherwise,
 263       * <tt>StateFactory.getStateToBind()</tt> is called.
 264       *</ul>
 265       *
 266       * Service providers that implement the <tt>DirContext</tt> interface
 267       * should use this method, not <tt>NamingManager.getStateToBind()</tt>.
 268       *<p>
 269       * See NamingManager.getStateToBind() for a description of how
 270       * the list of state factories to be tried is determined.
 271       *<p>
 272       * The object returned by this method is owned by the caller.
 273       * The implementation will not subsequently modify it.
 274       * It will contain either a new <tt>Attributes</tt> object that is
 275       * likewise owned by the caller, or a reference to the original
 276       * <tt>attrs</tt> parameter.
 277       *
 278       * @param obj The non-null object for which to get state to bind.
 279       * @param name The name of this object relative to <code>nameCtx</code>,
 280       *         or null if no name is specified.
 281       * @param nameCtx The context relative to which the <code>name</code>
 282       *         parameter is specified, or null if <code>name</code> is
 283       *         relative to the default initial context.
 284       * @param environment The possibly null environment to
 285       *         be used in the creation of the state factory and
 286       *         the object's state.
 287       * @param attrs The possibly null Attributes that is to be bound with the
 288       *         object.
 289       * @return A non-null DirStateFactory.Result containing
 290       *  the object and attributes to be bound.
 291       *  If no state factory returns a non-null answer, the result will contain
 292       *  the object (<tt>obj</tt>) itself with the original attributes.
 293       * @exception NamingException If a naming exception was encountered
 294       *         while using the factories.
 295       *         A factory should only throw an exception if it does not want
 296       *         other factories to be used in an attempt to create an object.
 297       *         See <tt>DirStateFactory.getStateToBind()</tt>.
 298       * @see DirStateFactory
 299       * @see DirStateFactory#getStateToBind
 300       * @see NamingManager#getStateToBind
 301       * @since 1.3
 302       */
 303     public static DirStateFactory.Result
 304         getStateToBind(Object obj, Name name, Context nameCtx,
 305                        Hashtable<?,?> environment, Attributes attrs)
 306         throws NamingException {
 307 
 308         // Get list of state factories
 309         FactoryEnumeration factories = ResourceManager.getFactories(
 310             Context.STATE_FACTORIES, environment, nameCtx);
 311 
 312         if (factories == null) {
 313             // no factories to try; just return originals
 314             return new DirStateFactory.Result(obj, attrs);
 315         }
 316 
 317         // Try each factory until one succeeds
 318         StateFactory factory;
 319         Object objanswer;
 320         DirStateFactory.Result answer = null;
 321         while (answer == null && factories.hasMore()) {
 322             factory = (StateFactory)factories.next();
 323             if (factory instanceof DirStateFactory) {
 324                 answer = ((DirStateFactory)factory).
 325                     getStateToBind(obj, name, nameCtx, environment, attrs);
 326             } else {
 327                 objanswer =
 328                     factory.getStateToBind(obj, name, nameCtx, environment);
 329                 if (objanswer != null) {
 330                     answer = new DirStateFactory.Result(objanswer, attrs);
 331                 }
 332             }
 333         }
 334 
 335         return (answer != null) ? answer :
 336             new DirStateFactory.Result(obj, attrs); // nothing new
 337     }
 338 }