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