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 }