001 /* NamingManager.java -- Creates contexts and objects
002 Copyright (C) 2000, 2001, 2002, 2003, 2004,
003 2006 Free Software Foundation, Inc.
004
005 This file is part of GNU Classpath.
006
007 GNU Classpath is free software; you can redistribute it and/or modify
008 it under the terms of the GNU General Public License as published by
009 the Free Software Foundation; either version 2, or (at your option)
010 any later version.
011
012 GNU Classpath is distributed in the hope that it will be useful, but
013 WITHOUT ANY WARRANTY; without even the implied warranty of
014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 General Public License for more details.
016
017 You should have received a copy of the GNU General Public License
018 along with GNU Classpath; see the file COPYING. If not, write to the
019 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020 02110-1301 USA.
021
022 Linking this library statically or dynamically with other modules is
023 making a combined work based on this library. Thus, the terms and
024 conditions of the GNU General Public License cover the whole
025 combination.
026
027 As a special exception, the copyright holders of this library give you
028 permission to link this library with independent modules to produce an
029 executable, regardless of the license terms of these independent
030 modules, and to copy and distribute the resulting executable under
031 terms of your choice, provided that you also meet, for each linked
032 independent module, the terms and conditions of the license of that
033 module. An independent module is a module which is not derived from
034 or based on this library. If you modify this library, you may extend
035 this exception to your version of the library, but you are not
036 obligated to do so. If you do not wish to do so, delete this
037 exception statement from your version. */
038
039
040 package javax.naming.spi;
041
042 import gnu.classpath.VMStackWalker;
043
044 import java.util.Enumeration;
045 import java.util.Hashtable;
046 import java.util.StringTokenizer;
047
048 import javax.naming.CannotProceedException;
049 import javax.naming.Context;
050 import javax.naming.Name;
051 import javax.naming.NamingException;
052 import javax.naming.NoInitialContextException;
053 import javax.naming.RefAddr;
054 import javax.naming.Reference;
055 import javax.naming.Referenceable;
056 import javax.naming.StringRefAddr;
057
058 /**
059 * Contains methods for creating contexts and objects referred to by
060 * location information. The location is specified in the scope of the
061 * certain naming or directory service. This class only contais static
062 * methods and cannot be instantiated.
063 */
064 public class NamingManager
065 {
066 /**
067 * The environment property into which getContinuationContext() stores the
068 * value of the CannotProceedException parameter. The value of this field
069 * is <i>java.naming.spi.CannotProceedException<i>.
070 */
071 public static final String CPE = "java.naming.spi.CannotProceedException";
072
073 private static InitialContextFactoryBuilder icfb;
074
075 // Package private so DirectoryManager can access it.
076 static ObjectFactoryBuilder ofb;
077
078 // This class cannot be instantiated.
079 NamingManager ()
080 {
081 }
082
083 /**
084 * Checks if the initial context factory builder has been set.
085 *
086 * @return true if the builder has been set
087 *
088 * @see #setInitialContextFactoryBuilder(InitialContextFactoryBuilder)
089 */
090 public static boolean hasInitialContextFactoryBuilder ()
091 {
092 return icfb != null;
093 }
094
095 /**
096 * Creates the initial context. If the initial object factory builder has
097 * been set with {@link #setObjectFactoryBuilder(ObjectFactoryBuilder)},
098 * the work is delegated to this builder. Otherwise, the method searches
099 * for the property Context.INITIAL_CONTEXT_FACTORY first in the passed
100 * table and then in the system properties. The value of this property is
101 * uses as a class name to install the context factory. The corresponding
102 * class must exist, be public and have the public parameterless constructor.
103 *
104 * @param environment the properties, used to create the context.
105 *
106 * @return the created context
107 *
108 * @throws NoInitialContextException if the initial builder is not set,
109 * the property Context.INITIAL_CONTEXT_FACTORY is missing of the
110 * class, named by this property, cannot be instantiated.
111 * @throws NamingException if throws by the context factory
112 */
113 public static Context getInitialContext (Hashtable<?, ?> environment)
114 throws NamingException
115 {
116 InitialContextFactory icf = null;
117
118 if (icfb != null)
119 icf = icfb.createInitialContextFactory(environment);
120 else
121 {
122 String java_naming_factory_initial = null;
123 if (environment != null)
124 java_naming_factory_initial
125 = (String) environment.get (Context.INITIAL_CONTEXT_FACTORY);
126 if (java_naming_factory_initial == null)
127 java_naming_factory_initial =
128 System.getProperty (Context.INITIAL_CONTEXT_FACTORY);
129 if (java_naming_factory_initial == null)
130 throw new
131 NoInitialContextException ("Can't find property: "
132 + Context.INITIAL_CONTEXT_FACTORY);
133
134 try
135 {
136 icf = (InitialContextFactory)Class.forName
137 (java_naming_factory_initial, true,
138 Thread.currentThread().getContextClassLoader())
139 .newInstance ();
140 }
141 catch (Exception exception)
142 {
143 NoInitialContextException e
144 = new NoInitialContextException
145 ("Can't load InitialContextFactory class: "
146 + java_naming_factory_initial);
147 e.setRootCause(exception);
148 throw e;
149 }
150 }
151
152 return icf.getInitialContext (environment);
153 }
154
155 /**
156 * <p>
157 * Creates the URL context for the given URL scheme id.
158 * </p>
159 * <p>
160 * The class name of the factory that creates the context has the naming
161 * pattern scheme-idURLContextFactory. For instance, the factory for the "ftp"
162 * sheme should be named "ftpURLContextFactory".
163 * </p>
164 * <p>
165 * The Context.URL_PKG_PREFIXES environment property contains the
166 * colon-separated list of the possible package prefixes. The package name is
167 * constructed concatenating the package prefix with the scheme id. This
168 * property is searched in the passed <i>environment</i> parameter and later
169 * in the system properties.
170 * </p>
171 * <p>
172 * If the factory class cannot be found in the specified packages, system will
173 * try to use the default internal factory for the given scheme.
174 * </p>
175 * <p>
176 * After the factory is instantiated, its method
177 * {@link ObjectFactory#getObjectInstance(Object, Name, Context, Hashtable)}
178 * is called to create and return the object instance.
179 *
180 * @param refInfo passed to the factory
181 * @param name passed to the factory
182 * @param nameCtx passed to the factory
183 * @param scheme the url scheme that must be supported by the given context
184 * @param environment the properties for creating the factory and context (may
185 * be null)
186 * @return the created context
187 * @throws NamingException if thrown by the factory when creating the context.
188 */
189 static Context getURLContext(Object refInfo, Name name, Context nameCtx,
190 String scheme, Hashtable<?,?> environment)
191 throws NamingException
192 {
193 // Doc specifies com.sun.jndi.url as the final destination, but we cannot
194 // put our classes into such namespace.
195 String defaultPrefix = "gnu.javax.naming.jndi.url";
196
197 // The final default location, as specified in the documentation.
198 String finalPrefix = "com.sun.jndi.url";
199
200 StringBuffer allPrefixes = new StringBuffer();
201
202 String prefixes;
203 if (environment != null)
204 {
205 prefixes = (String) environment.get(Context.URL_PKG_PREFIXES);
206 if (prefixes != null)
207 allPrefixes.append(prefixes);
208 }
209
210 prefixes = System.getProperty(Context.URL_PKG_PREFIXES);
211 if (prefixes != null)
212 {
213 if (allPrefixes.length() > 0)
214 allPrefixes.append(':');
215 allPrefixes.append(prefixes);
216 }
217
218 if (allPrefixes.length() > 0)
219 allPrefixes.append(':');
220 allPrefixes.append(defaultPrefix);
221 allPrefixes.append(':');
222 allPrefixes.append(finalPrefix);
223
224 scheme = scheme + "." + scheme + "URLContextFactory";
225
226 StringTokenizer tokens = new StringTokenizer(allPrefixes.toString(), ":");
227 while (tokens.hasMoreTokens())
228 {
229 String aTry = tokens.nextToken();
230 try
231 {
232 String tryClass = aTry + "." + scheme;
233 Class factoryClass = forName(tryClass);
234 if (factoryClass != null)
235 {
236 Object obj;
237 try
238 {
239 ObjectFactory factory = (ObjectFactory) factoryClass.newInstance();
240 obj = factory.getObjectInstance(refInfo, name, nameCtx,
241 environment);
242 Context ctx = (Context) obj;
243 if (ctx != null)
244 return ctx;
245 }
246 catch (RuntimeException e)
247 {
248 // TODO Auto-generated catch block
249 e.printStackTrace();
250 }
251 }
252 }
253 catch (ClassNotFoundException _1)
254 {
255 // Ignore it.
256 }
257 catch (ClassCastException _2)
258 {
259 // This means that the class we found was not an
260 // ObjectFactory or that the factory returned something
261 // which was not a Context.
262 }
263 catch (InstantiationException _3)
264 {
265 // If we couldn't instantiate the factory we might get
266 // this.
267 }
268 catch (IllegalAccessException _4)
269 {
270 // Another possibility when instantiating.
271 }
272 catch (NamingException _5)
273 {
274 throw _5;
275 }
276 catch (Exception _6)
277 {
278 // Anything from getObjectInstance.
279 }
280 }
281
282 return null;
283 }
284
285 /**
286 * Load the class with the given name. This method tries to use the context
287 * class loader first. If this fails, it searches for the suitable class
288 * loader in the caller stack trace. This method is a central point where all
289 * requests to find a class by name are delegated.
290 */
291 static Class forName(String className)
292 {
293 try
294 {
295 return Class.forName(className, true,
296 Thread.currentThread().getContextClassLoader());
297 }
298 catch (ClassNotFoundException nex)
299 {
300 /**
301 * Returns the first user defined class loader on the call stack, or
302 * null when no non-null class loader was found.
303 */
304 Class[] ctx = VMStackWalker.getClassContext();
305 for (int i = 0; i < ctx.length; i++)
306 {
307 // Since we live in a class loaded by the bootstrap
308 // class loader, getClassLoader is safe to call without
309 // needing to be wrapped in a privileged action.
310 ClassLoader cl = ctx[i].getClassLoader();
311 try
312 {
313 if (cl != null)
314 return Class.forName(className, true, cl);
315 }
316 catch (ClassNotFoundException nex2)
317 {
318 // Try next.
319 }
320 }
321 }
322 return null;
323 }
324
325
326 /**
327 * <p>
328 * Creates the URL context for the given URL scheme id.
329 * </p>
330 * <p>
331 * The class name of the factory that creates the context has the naming
332 * pattern scheme-idURLContextFactory. For instance, the factory for the
333 * "ftp" scheme should be named "ftpURLContextFactory".
334 * The Context.URL_PKG_PREFIXES environment property contains the
335 * colon-separated list of the possible package prefixes. The package name
336 * is constructed by concatenating the package prefix with the scheme id.
337 * </p>
338 * <p>
339 * If the factory class cannot be found in the specified packages, the
340 * system will try to use the default internal factory for the given scheme.
341 * </p>
342 * <p>
343 * After the factory is instantiated, its method
344 * {@link ObjectFactory#getObjectInstance(Object, Name, Context, Hashtable)}
345 * is called to create and return the object instance.
346 *
347 * @param scheme the url scheme that must be supported by the given context
348 * @param environment the properties for creating the factory and context
349 * (may be null)
350 * @return the created context
351 * @throws NamingException if thrown by the factory when creating the
352 * context.
353 */
354 public static Context getURLContext (String scheme,
355 Hashtable<?, ?> environment)
356 throws NamingException
357 {
358 return getURLContext (null, null, null, scheme, environment);
359 }
360
361 /**
362 * Sets the initial object factory builder.
363 *
364 * @param builder the builder to set
365 *
366 * @throws SecurityException if the builder cannot be installed due
367 * security restrictions.
368 * @throws NamingException if the builder cannot be installed due other
369 * reasons
370 * @throws IllegalStateException if setting the builder repeatedly
371 */
372 public static void setObjectFactoryBuilder (ObjectFactoryBuilder builder)
373 throws NamingException
374 {
375 SecurityManager sm = System.getSecurityManager ();
376 if (sm != null)
377 sm.checkSetFactory ();
378 // Once the builder is installed it cannot be replaced.
379 if (ofb != null)
380 throw new IllegalStateException ("object factory builder already installed");
381 if (builder != null)
382 ofb = builder;
383 }
384
385 static StringTokenizer getPlusPath (String property, Hashtable env,
386 Context nameCtx)
387 throws NamingException
388 {
389 String path = (String) env.get (property);
390 if (nameCtx == null)
391 nameCtx = getInitialContext (env);
392 String path2 = (String) nameCtx.getEnvironment ().get (property);
393 if (path == null)
394 path = path2;
395 else if (path2 != null)
396 path += ":" + path2;
397 return new StringTokenizer (path != null ? path : "", ":");
398 }
399
400 /**
401 * <p>Creates an object for the specified name context, environment and
402 * referencing context object.</p>
403 * <p>
404 * If the builder factory is set by
405 * {@link #setObjectFactoryBuilder(ObjectFactoryBuilder)}, the call is
406 * delegated to that factory. Otherwise, the object is created using the
407 * following rules:
408 * <ul>
409 * <li>If the referencing object (refInfo) contains the factory class name,
410 * the object is created by this factory. If the creation fails,
411 * the parameter refInfo is returned as the method return value.</li>
412 * <li>If the referencing object has no factory class name, and the addresses
413 * are StringRefAddrs having the address type "URL", the object is
414 * created by the URL context factory. The used factory corresponds the
415 * the naming schema of the each URL. If the attempt to create
416 * the object this way is not successful, the subsequent rule is
417 * tried.</li>
418 * <li> If the refInfo is not an instance of Reference or Referencable
419 * (for example, null), the object is created by the factories,
420 * specified in the Context.OBJECT_FACTORIES property of the
421 * environment and the provider resource file, associated with the
422 * nameCtx. The value of this property is the colon separated list
423 * of the possible factories. If none of the factories can be
424 * loaded, the refInfo is returned.
425 * </ul>
426 * </p>
427 * <p>The object factory must be public and have the public parameterless
428 * constructor.</p>
429 *
430 * @param refInfo the referencing object, for which the new object must be
431 * created (can be null). If not null, it is usually an instance of
432 * the {@link Reference} or {@link Referenceable}.
433 * @param name the name of the object. The name is relative to
434 * the nameCtx naming context. The value of this parameter can be
435 * null if the name is not specified.
436 * @param nameCtx the naming context, in which scope the name of the new
437 * object is specified. If this parameter is null, the name is
438 * specified in the scope of the initial context.
439 * @param environment contains additional information for creating the object.
440 * This paramter can be null if there is no need to provide any
441 * additional information.
442 *
443 * @return the created object. If the creation fails, in some cases
444 * the parameter refInfo may be returned.
445 *
446 * @throws NamingException if the attempt to name the new object has failed
447 * @throws Exception if the object factory throws it. The object factory
448 * only throws an exception if it does not want other factories
449 * to be used to create the object.
450 */
451 public static Object getObjectInstance (Object refInfo,
452 Name name,
453 Context nameCtx,
454 Hashtable<?, ?> environment)
455 throws Exception
456 {
457 ObjectFactory factory = null;
458
459 if (ofb != null)
460 factory = ofb.createObjectFactory (refInfo, environment);
461 else
462 {
463 // First see if we have a Reference or a Referenceable. If so
464 // we do some special processing.
465 Object ref2 = refInfo;
466 if (refInfo instanceof Referenceable)
467 ref2 = ((Referenceable) refInfo).getReference ();
468 if (ref2 instanceof Reference)
469 {
470 Reference ref = (Reference) ref2;
471
472 // If we have a factory class name then we use that.
473 String fClass = ref.getFactoryClassName ();
474 if (fClass != null)
475 {
476 // Exceptions here are passed to the caller.
477 Class k = Class.forName (fClass,
478 true,
479 Thread.currentThread().getContextClassLoader());
480 factory = (ObjectFactory) k.newInstance ();
481 }
482 else
483 {
484 // There's no factory class name. If the address is a
485 // StringRefAddr with address type `URL', then we try
486 // the URL's context factory.
487 Enumeration e = ref.getAll ();
488 while (e.hasMoreElements ())
489 {
490 RefAddr ra = (RefAddr) e.nextElement ();
491 if (ra instanceof StringRefAddr
492 && "URL".equals (ra.getType ()))
493 {
494 factory
495 = (ObjectFactory) getURLContext (refInfo,
496 name,
497 nameCtx,
498 (String) ra.getContent (),
499 environment);
500 Object obj = factory.getObjectInstance (refInfo,
501 name,
502 nameCtx,
503 environment);
504 if (obj != null)
505 return obj;
506 }
507 }
508
509 // Have to try the next step.
510 factory = null;
511 }
512 }
513
514 // Now look at OBJECT_FACTORIES to find the factory.
515 if (factory == null)
516 {
517 StringTokenizer tokens = getPlusPath (Context.OBJECT_FACTORIES,
518 environment, nameCtx);
519
520 while (tokens.hasMoreTokens ())
521 {
522 String klassName = tokens.nextToken ();
523 Class k = Class.forName (klassName,
524 true,
525 Thread.currentThread().getContextClassLoader());
526 factory = (ObjectFactory) k.newInstance ();
527 Object obj = factory.getObjectInstance (refInfo, name,
528 nameCtx, environment);
529 if (obj != null)
530 return obj;
531 }
532
533 // Failure.
534 return refInfo;
535 }
536 }
537
538 if (factory == null)
539 return refInfo;
540 Object obj = factory.getObjectInstance (refInfo, name,
541 nameCtx, environment);
542 return obj == null ? refInfo : obj;
543 }
544
545 /**
546 * Sets the initial context factory builder.
547 *
548 * @param builder the builder to set
549 *
550 * @throws SecurityException if the builder cannot be installed due
551 * security restrictions.
552 * @throws NamingException if the builder cannot be installed due other
553 * reasons
554 * @throws IllegalStateException if setting the builder repeatedly
555 *
556 * @see #hasInitialContextFactoryBuilder()
557 */
558 public static void setInitialContextFactoryBuilder
559 (InitialContextFactoryBuilder builder)
560 throws NamingException
561 {
562 SecurityManager sm = System.getSecurityManager ();
563 if (sm != null)
564 sm.checkSetFactory ();
565 // Once the builder is installed it cannot be replaced.
566 if (icfb != null)
567 throw new IllegalStateException ("ctx factory builder already installed");
568 if (builder != null)
569 icfb = builder;
570 }
571
572 /**
573 * Creates a context in which the context operation must be continued.
574 * This method is used by operations on names that span multiple namespaces.
575 *
576 * @param cpe the exception that triggered this continuation. This method
577 * obtains the environment ({@link CannotProceedException#getEnvironment()}
578 * and sets the environment property {@link #CPE} = cpe.
579 *
580 * @return a non null context for continuing the operation
581 *
582 * @throws NamingException if the naming problems have occured
583 */
584 public static Context getContinuationContext (CannotProceedException cpe)
585 throws NamingException
586 {
587 Hashtable env = cpe.getEnvironment ();
588 if (env != null)
589 env.put (CPE, cpe);
590
591 // TODO: Check if this implementation matches the API specification
592 try
593 {
594 Object obj = getObjectInstance (cpe.getResolvedObj(),
595 cpe.getAltName (),
596 cpe.getAltNameCtx (),
597 env);
598 if (obj != null)
599 return (Context) obj;
600 }
601 catch (Exception _)
602 {
603 }
604
605 // fix stack trace for re-thrown exception (message confusing otherwise)
606 cpe.fillInStackTrace();
607
608 throw cpe;
609 }
610
611 /**
612 * Get the object state for binding.
613 *
614 * @param obj the object, for that the binding state must be retrieved. Cannot
615 * be null.
616 * @param name the name of this object, related to the nameCtx. Can be null if
617 * not specified.
618 * @param nameCtx the naming context, to that the object name is related. Can
619 * be null if the name is related to the initial default context.
620 * @param environment the properties for creating the object state. Can be
621 * null if no properties are provided.
622 * @return the object state for binding, may be null if no changes are
623 * returned by the factory
624 * @throws NamingException
625 */
626 public static Object getStateToBind (Object obj, Name name,
627 Context nameCtx, Hashtable<?, ?> environment)
628 throws NamingException
629 {
630 StringTokenizer tokens = getPlusPath (Context.STATE_FACTORIES,
631 environment, nameCtx);
632 while (tokens.hasMoreTokens ())
633 {
634 String klassName = tokens.nextToken ();
635 try
636 {
637 Class k = Class.forName (klassName,
638 true,
639 Thread.currentThread().getContextClassLoader());
640 StateFactory factory = (StateFactory) k.newInstance ();
641 Object o = factory.getStateToBind (obj, name, nameCtx,
642 environment);
643 if (o != null)
644 return o;
645 }
646 catch (ClassNotFoundException _1)
647 {
648 // Ignore it.
649 }
650 catch (ClassCastException _2)
651 {
652 // This means that the class we found was not an
653 // ObjectFactory or that the factory returned something
654 // which was not a Context.
655 }
656 catch (InstantiationException _3)
657 {
658 // If we couldn't instantiate the factory we might get
659 // this.
660 }
661 catch (IllegalAccessException _4)
662 {
663 // Another possibility when instantiating.
664 }
665 }
666
667 return obj;
668 }
669 }