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.security.sasl; 27 28 import javax.security.auth.callback.CallbackHandler; 29 30 import java.util.Enumeration; 31 import java.util.Iterator; 32 import java.util.Map; 33 import java.util.Set; 34 import java.util.HashSet; 35 import java.util.Collections; 36 import java.security.Provider; 37 import java.security.Security; 38 39 /** 40 * A static class for creating SASL clients and servers. 41 *<p> 42 * This class defines the policy of how to locate, load, and instantiate 43 * SASL clients and servers. 44 *<p> 45 * For example, an application or library gets a SASL client by doing 46 * something like: 47 *<blockquote><pre> 48 * SaslClient sc = Sasl.createSaslClient(mechanisms, 49 * authorizationId, protocol, serverName, props, callbackHandler); 50 *</pre></blockquote> 51 * It can then proceed to use the instance to create an authentication connection. 52 *<p> 53 * Similarly, a server gets a SASL server by using code that looks as follows: 54 *<blockquote><pre> 55 * SaslServer ss = Sasl.createSaslServer(mechanism, 56 * protocol, serverName, props, callbackHandler); 57 *</pre></blockquote> 58 * 59 * @since 1.5 60 * 61 * @author Rosanna Lee 62 * @author Rob Weltman 63 */ 64 public class Sasl { 65 // Cannot create one of these 66 private Sasl() { 67 } 68 69 /** 70 * The name of a property that specifies the quality-of-protection to use. 71 * The property contains a comma-separated, ordered list 72 * of quality-of-protection values that the 73 * client or server is willing to support. A qop value is one of 74 * <ul> 75 * <li><tt>"auth"</tt> - authentication only</li> 76 * <li><tt>"auth-int"</tt> - authentication plus integrity protection</li> 77 * <li><tt>"auth-conf"</tt> - authentication plus integrity and confidentiality 78 * protection</li> 79 * </ul> 80 * 81 * The order of the list specifies the preference order of the client or 82 * server. If this property is absent, the default qop is <tt>"auth"</tt>. 83 * The value of this constant is <tt>"javax.security.sasl.qop"</tt>. 84 */ 85 public static final String QOP = "javax.security.sasl.qop"; 86 87 /** 88 * The name of a property that specifies the cipher strength to use. 89 * The property contains a comma-separated, ordered list 90 * of cipher strength values that 91 * the client or server is willing to support. A strength value is one of 92 * <ul> 93 * <li><tt>"low"</tt></li> 94 * <li><tt>"medium"</tt></li> 95 * <li><tt>"high"</tt></li> 96 * </ul> 97 * The order of the list specifies the preference order of the client or 98 * server. An implementation should allow configuration of the meaning 99 * of these values. An application may use the Java Cryptography 100 * Extension (JCE) with JCE-aware mechanisms to control the selection of 101 * cipher suites that match the strength values. 102 * <BR> 103 * If this property is absent, the default strength is 104 * <tt>"high,medium,low"</tt>. 105 * The value of this constant is <tt>"javax.security.sasl.strength"</tt>. 106 */ 107 public static final String STRENGTH = "javax.security.sasl.strength"; 108 109 /** 110 * The name of a property that specifies whether the 111 * server must authenticate to the client. The property contains 112 * <tt>"true"</tt> if the server must 113 * authenticate the to client; <tt>"false"</tt> otherwise. 114 * The default is <tt>"false"</tt>. 115 * <br>The value of this constant is 116 * <tt>"javax.security.sasl.server.authentication"</tt>. 117 */ 118 public static final String SERVER_AUTH = 119 "javax.security.sasl.server.authentication"; 120 121 /** 122 * The name of a property that specifies the bound server name for 123 * an unbound server. A server is created as an unbound server by setting 124 * the {@code serverName} argument in {@link #createSaslServer} as null. 125 * The property contains the bound host name after the authentication 126 * exchange has completed. It is only available on the server side. 127 * <br>The value of this constant is 128 * <tt>"javax.security.sasl.bound.server.name"</tt>. 129 */ 130 public static final String BOUND_SERVER_NAME = 131 "javax.security.sasl.bound.server.name"; 132 133 /** 134 * The name of a property that specifies the maximum size of the receive 135 * buffer in bytes of <tt>SaslClient</tt>/<tt>SaslServer</tt>. 136 * The property contains the string representation of an integer. 137 * <br>If this property is absent, the default size 138 * is defined by the mechanism. 139 * <br>The value of this constant is <tt>"javax.security.sasl.maxbuffer"</tt>. 140 */ 141 public static final String MAX_BUFFER = "javax.security.sasl.maxbuffer"; 142 143 /** 144 * The name of a property that specifies the maximum size of the raw send 145 * buffer in bytes of <tt>SaslClient</tt>/<tt>SaslServer</tt>. 146 * The property contains the string representation of an integer. 147 * The value of this property is negotiated between the client and server 148 * during the authentication exchange. 149 * <br>The value of this constant is <tt>"javax.security.sasl.rawsendsize"</tt>. 150 */ 151 public static final String RAW_SEND_SIZE = "javax.security.sasl.rawsendsize"; 152 153 /** 154 * The name of a property that specifies whether to reuse previously 155 * authenticated session information. The property contains "true" if the 156 * mechanism implementation may attempt to reuse previously authenticated 157 * session information; it contains "false" if the implementation must 158 * not reuse previously authenticated session information. A setting of 159 * "true" serves only as a hint: it does not necessarily entail actual 160 * reuse because reuse might not be possible due to a number of reasons, 161 * including, but not limited to, lack of mechanism support for reuse, 162 * expiration of reusable information, and the peer's refusal to support 163 * reuse. 164 * 165 * The property's default value is "false". The value of this constant 166 * is "javax.security.sasl.reuse". 167 * 168 * Note that all other parameters and properties required to create a 169 * SASL client/server instance must be provided regardless of whether 170 * this property has been supplied. That is, you cannot supply any less 171 * information in anticipation of reuse. 172 * 173 * Mechanism implementations that support reuse might allow customization 174 * of its implementation, for factors such as cache size, timeouts, and 175 * criteria for reuseability. Such customizations are 176 * implementation-dependent. 177 */ 178 public static final String REUSE = "javax.security.sasl.reuse"; 179 180 /** 181 * The name of a property that specifies 182 * whether mechanisms susceptible to simple plain passive attacks (e.g., 183 * "PLAIN") are not permitted. The property 184 * contains <tt>"true"</tt> if such mechanisms are not permitted; 185 * <tt>"false"</tt> if such mechanisms are permitted. 186 * The default is <tt>"false"</tt>. 187 * <br>The value of this constant is 188 * <tt>"javax.security.sasl.policy.noplaintext"</tt>. 189 */ 190 public static final String POLICY_NOPLAINTEXT = 191 "javax.security.sasl.policy.noplaintext"; 192 193 /** 194 * The name of a property that specifies whether 195 * mechanisms susceptible to active (non-dictionary) attacks 196 * are not permitted. 197 * The property contains <tt>"true"</tt> 198 * if mechanisms susceptible to active attacks 199 * are not permitted; <tt>"false"</tt> if such mechanisms are permitted. 200 * The default is <tt>"false"</tt>. 201 * <br>The value of this constant is 202 * <tt>"javax.security.sasl.policy.noactive"</tt>. 203 */ 204 public static final String POLICY_NOACTIVE = 205 "javax.security.sasl.policy.noactive"; 206 207 /** 208 * The name of a property that specifies whether 209 * mechanisms susceptible to passive dictionary attacks are not permitted. 210 * The property contains <tt>"true"</tt> 211 * if mechanisms susceptible to dictionary attacks are not permitted; 212 * <tt>"false"</tt> if such mechanisms are permitted. 213 * The default is <tt>"false"</tt>. 214 *<br> 215 * The value of this constant is 216 * <tt>"javax.security.sasl.policy.nodictionary"</tt>. 217 */ 218 public static final String POLICY_NODICTIONARY = 219 "javax.security.sasl.policy.nodictionary"; 220 221 /** 222 * The name of a property that specifies whether mechanisms that accept 223 * anonymous login are not permitted. The property contains <tt>"true"</tt> 224 * if mechanisms that accept anonymous login are not permitted; 225 * <tt>"false"</tt> 226 * if such mechanisms are permitted. The default is <tt>"false"</tt>. 227 *<br> 228 * The value of this constant is 229 * <tt>"javax.security.sasl.policy.noanonymous"</tt>. 230 */ 231 public static final String POLICY_NOANONYMOUS = 232 "javax.security.sasl.policy.noanonymous"; 233 234 /** 235 * The name of a property that specifies whether mechanisms that implement 236 * forward secrecy between sessions are required. Forward secrecy 237 * means that breaking into one session will not automatically 238 * provide information for breaking into future sessions. 239 * The property 240 * contains <tt>"true"</tt> if mechanisms that implement forward secrecy 241 * between sessions are required; <tt>"false"</tt> if such mechanisms 242 * are not required. The default is <tt>"false"</tt>. 243 *<br> 244 * The value of this constant is 245 * <tt>"javax.security.sasl.policy.forward"</tt>. 246 */ 247 public static final String POLICY_FORWARD_SECRECY = 248 "javax.security.sasl.policy.forward"; 249 250 /** 251 * The name of a property that specifies whether 252 * mechanisms that pass client credentials are required. The property 253 * contains <tt>"true"</tt> if mechanisms that pass 254 * client credentials are required; <tt>"false"</tt> 255 * if such mechanisms are not required. The default is <tt>"false"</tt>. 256 *<br> 257 * The value of this constant is 258 * <tt>"javax.security.sasl.policy.credentials"</tt>. 259 */ 260 public static final String POLICY_PASS_CREDENTIALS = 261 "javax.security.sasl.policy.credentials"; 262 263 /** 264 * The name of a property that specifies the credentials to use. 265 * The property contains a mechanism-specific Java credential object. 266 * Mechanism implementations may examine the value of this property 267 * to determine whether it is a class that they support. 268 * The property may be used to supply credentials to a mechanism that 269 * supports delegated authentication. 270 *<br> 271 * The value of this constant is 272 * <tt>"javax.security.sasl.credentials"</tt>. 273 */ 274 public static final String CREDENTIALS = "javax.security.sasl.credentials"; 275 276 /** 277 * Creates a <tt>SaslClient</tt> using the parameters supplied. 278 * 279 * This method uses the 280 <a href="{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#Provider">JCA Security Provider Framework</a>, described in the 281 * "Java Cryptography Architecture API Specification & Reference", for 282 * locating and selecting a <tt>SaslClient</tt> implementation. 283 * 284 * First, it 285 * obtains an ordered list of <tt>SaslClientFactory</tt> instances from 286 * the registered security providers for the "SaslClientFactory" service 287 * and the specified SASL mechanism(s). It then invokes 288 * <tt>createSaslClient()</tt> on each factory instance on the list 289 * until one produces a non-null <tt>SaslClient</tt> instance. It returns 290 * the non-null <tt>SaslClient</tt> instance, or null if the search fails 291 * to produce a non-null <tt>SaslClient</tt> instance. 292 *<p> 293 * A security provider for SaslClientFactory registers with the 294 * JCA Security Provider Framework keys of the form <br> 295 * <tt>SaslClientFactory.<em>mechanism_name</em></tt> 296 * <br> 297 * and values that are class names of implementations of 298 * <tt>javax.security.sasl.SaslClientFactory</tt>. 299 * 300 * For example, a provider that contains a factory class, 301 * <tt>com.wiz.sasl.digest.ClientFactory</tt>, that supports the 302 * "DIGEST-MD5" mechanism would register the following entry with the JCA: 303 * <tt>SaslClientFactory.DIGEST-MD5 com.wiz.sasl.digest.ClientFactory</tt> 304 *<p> 305 * See the 306 * "Java Cryptography Architecture API Specification & Reference" 307 * for information about how to install and configure security service 308 * providers. 309 * 310 * @param mechanisms The non-null list of mechanism names to try. Each is the 311 * IANA-registered name of a SASL mechanism. (e.g. "GSSAPI", "CRAM-MD5"). 312 * @param authorizationId The possibly null protocol-dependent 313 * identification to be used for authorization. 314 * If null or empty, the server derives an authorization 315 * ID from the client's authentication credentials. 316 * When the SASL authentication completes successfully, 317 * the specified entity is granted access. 318 * 319 * @param protocol The non-null string name of the protocol for which 320 * the authentication is being performed (e.g., "ldap"). 321 * 322 * @param serverName The non-null fully-qualified host name of the server 323 * to authenticate to. 324 * 325 * @param props The possibly null set of properties used to 326 * select the SASL mechanism and to configure the authentication 327 * exchange of the selected mechanism. 328 * For example, if <tt>props</tt> contains the 329 * <code>Sasl.POLICY_NOPLAINTEXT</code> property with the value 330 * <tt>"true"</tt>, then the selected 331 * SASL mechanism must not be susceptible to simple plain passive attacks. 332 * In addition to the standard properties declared in this class, 333 * other, possibly mechanism-specific, properties can be included. 334 * Properties not relevant to the selected mechanism are ignored, 335 * including any map entries with non-String keys. 336 * 337 * @param cbh The possibly null callback handler to used by the SASL 338 * mechanisms to get further information from the application/library 339 * to complete the authentication. For example, a SASL mechanism might 340 * require the authentication ID, password and realm from the caller. 341 * The authentication ID is requested by using a <tt>NameCallback</tt>. 342 * The password is requested by using a <tt>PasswordCallback</tt>. 343 * The realm is requested by using a <tt>RealmChoiceCallback</tt> if there is a list 344 * of realms to choose from, and by using a <tt>RealmCallback</tt> if 345 * the realm must be entered. 346 * 347 *@return A possibly null <tt>SaslClient</tt> created using the parameters 348 * supplied. If null, cannot find a <tt>SaslClientFactory</tt> 349 * that will produce one. 350 *@exception SaslException If cannot create a <tt>SaslClient</tt> because 351 * of an error. 352 */ 353 public static SaslClient createSaslClient( 354 String[] mechanisms, 355 String authorizationId, 356 String protocol, 357 String serverName, 358 Map<String,?> props, 359 CallbackHandler cbh) throws SaslException { 360 361 SaslClient mech = null; 362 SaslClientFactory fac; 363 String className; 364 String mechName; 365 366 for (int i = 0; i < mechanisms.length; i++) { 367 if ((mechName=mechanisms[i]) == null) { 368 throw new NullPointerException( 369 "Mechanism name cannot be null"); 370 } else if (mechName.length() == 0) { 371 continue; 372 } 373 String mechFilter = "SaslClientFactory." + mechName; 374 Provider[] provs = Security.getProviders(mechFilter); 375 for (int j = 0; provs != null && j < provs.length; j++) { 376 className = provs[j].getProperty(mechFilter); 377 if (className == null) { 378 // Case is ignored 379 continue; 380 } 381 382 fac = (SaslClientFactory) loadFactory(provs[j], className); 383 if (fac != null) { 384 mech = fac.createSaslClient( 385 new String[]{mechanisms[i]}, authorizationId, 386 protocol, serverName, props, cbh); 387 if (mech != null) { 388 return mech; 389 } 390 } 391 } 392 } 393 394 return null; 395 } 396 397 private static Object loadFactory(Provider p, String className) 398 throws SaslException { 399 try { 400 /* 401 * Load the implementation class with the same class loader 402 * that was used to load the provider. 403 * In order to get the class loader of a class, the 404 * caller's class loader must be the same as or an ancestor of 405 * the class loader being returned. Otherwise, the caller must 406 * have "getClassLoader" permission, or a SecurityException 407 * will be thrown. 408 */ 409 ClassLoader cl = p.getClass().getClassLoader(); 410 Class<?> implClass; 411 implClass = Class.forName(className, true, cl); 412 return implClass.newInstance(); 413 } catch (ClassNotFoundException e) { 414 throw new SaslException("Cannot load class " + className, e); 415 } catch (InstantiationException e) { 416 throw new SaslException("Cannot instantiate class " + className, e); 417 } catch (IllegalAccessException e) { 418 throw new SaslException("Cannot access class " + className, e); 419 } catch (SecurityException e) { 420 throw new SaslException("Cannot access class " + className, e); 421 } 422 } 423 424 425 /** 426 * Creates a <tt>SaslServer</tt> for the specified mechanism. 427 * 428 * This method uses the 429 <a href="{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#Provider">JCA Security Provider Framework</a>, 430 * described in the 431 * "Java Cryptography Architecture API Specification & Reference", for 432 * locating and selecting a <tt>SaslServer</tt> implementation. 433 * 434 * First, it 435 * obtains an ordered list of <tt>SaslServerFactory</tt> instances from 436 * the registered security providers for the "SaslServerFactory" service 437 * and the specified mechanism. It then invokes 438 * <tt>createSaslServer()</tt> on each factory instance on the list 439 * until one produces a non-null <tt>SaslServer</tt> instance. It returns 440 * the non-null <tt>SaslServer</tt> instance, or null if the search fails 441 * to produce a non-null <tt>SaslServer</tt> instance. 442 *<p> 443 * A security provider for SaslServerFactory registers with the 444 * JCA Security Provider Framework keys of the form <br> 445 * <tt>SaslServerFactory.<em>mechanism_name</em></tt> 446 * <br> 447 * and values that are class names of implementations of 448 * <tt>javax.security.sasl.SaslServerFactory</tt>. 449 * 450 * For example, a provider that contains a factory class, 451 * <tt>com.wiz.sasl.digest.ServerFactory</tt>, that supports the 452 * "DIGEST-MD5" mechanism would register the following entry with the JCA: 453 * <tt>SaslServerFactory.DIGEST-MD5 com.wiz.sasl.digest.ServerFactory</tt> 454 *<p> 455 * See the 456 * "Java Cryptography Architecture API Specification & Reference" 457 * for information about how to install and configure security 458 * service providers. 459 * 460 * @param mechanism The non-null mechanism name. It must be an 461 * IANA-registered name of a SASL mechanism. (e.g. "GSSAPI", "CRAM-MD5"). 462 * @param protocol The non-null string name of the protocol for which 463 * the authentication is being performed (e.g., "ldap"). 464 * @param serverName The fully qualified host name of the server, or null 465 * if the server is not bound to any specific host name. If the mechanism 466 * does not allow an unbound server, a <code>SaslException</code> will 467 * be thrown. 468 * @param props The possibly null set of properties used to 469 * select the SASL mechanism and to configure the authentication 470 * exchange of the selected mechanism. 471 * For example, if <tt>props</tt> contains the 472 * <code>Sasl.POLICY_NOPLAINTEXT</code> property with the value 473 * <tt>"true"</tt>, then the selected 474 * SASL mechanism must not be susceptible to simple plain passive attacks. 475 * In addition to the standard properties declared in this class, 476 * other, possibly mechanism-specific, properties can be included. 477 * Properties not relevant to the selected mechanism are ignored, 478 * including any map entries with non-String keys. 479 * 480 * @param cbh The possibly null callback handler to used by the SASL 481 * mechanisms to get further information from the application/library 482 * to complete the authentication. For example, a SASL mechanism might 483 * require the authentication ID, password and realm from the caller. 484 * The authentication ID is requested by using a <tt>NameCallback</tt>. 485 * The password is requested by using a <tt>PasswordCallback</tt>. 486 * The realm is requested by using a <tt>RealmChoiceCallback</tt> if there is a list 487 * of realms to choose from, and by using a <tt>RealmCallback</tt> if 488 * the realm must be entered. 489 * 490 *@return A possibly null <tt>SaslServer</tt> created using the parameters 491 * supplied. If null, cannot find a <tt>SaslServerFactory</tt> 492 * that will produce one. 493 *@exception SaslException If cannot create a <tt>SaslServer</tt> because 494 * of an error. 495 **/ 496 public static SaslServer 497 createSaslServer(String mechanism, 498 String protocol, 499 String serverName, 500 Map<String,?> props, 501 javax.security.auth.callback.CallbackHandler cbh) 502 throws SaslException { 503 504 SaslServer mech = null; 505 SaslServerFactory fac; 506 String className; 507 508 if (mechanism == null) { 509 throw new NullPointerException("Mechanism name cannot be null"); 510 } else if (mechanism.length() == 0) { 511 return null; 512 } 513 514 String mechFilter = "SaslServerFactory." + mechanism; 515 Provider[] provs = Security.getProviders(mechFilter); 516 for (int j = 0; provs != null && j < provs.length; j++) { 517 className = provs[j].getProperty(mechFilter); 518 if (className == null) { 519 throw new SaslException("Provider does not support " + 520 mechFilter); 521 } 522 fac = (SaslServerFactory) loadFactory(provs[j], className); 523 if (fac != null) { 524 mech = fac.createSaslServer( 525 mechanism, protocol, serverName, props, cbh); 526 if (mech != null) { 527 return mech; 528 } 529 } 530 } 531 532 return null; 533 } 534 535 /** 536 * Gets an enumeration of known factories for producing <tt>SaslClient</tt>. 537 * This method uses the same algorithm for locating factories as 538 * <tt>createSaslClient()</tt>. 539 * @return A non-null enumeration of known factories for producing 540 * <tt>SaslClient</tt>. 541 * @see #createSaslClient 542 */ 543 public static Enumeration<SaslClientFactory> getSaslClientFactories() { 544 Set<Object> facs = getFactories("SaslClientFactory"); 545 final Iterator<Object> iter = facs.iterator(); 546 return new Enumeration<SaslClientFactory>() { 547 public boolean hasMoreElements() { 548 return iter.hasNext(); 549 } 550 public SaslClientFactory nextElement() { 551 return (SaslClientFactory)iter.next(); 552 } 553 }; 554 } 555 556 /** 557 * Gets an enumeration of known factories for producing <tt>SaslServer</tt>. 558 * This method uses the same algorithm for locating factories as 559 * <tt>createSaslServer()</tt>. 560 * @return A non-null enumeration of known factories for producing 561 * <tt>SaslServer</tt>. 562 * @see #createSaslServer 563 */ 564 public static Enumeration<SaslServerFactory> getSaslServerFactories() { 565 Set<Object> facs = getFactories("SaslServerFactory"); 566 final Iterator<Object> iter = facs.iterator(); 567 return new Enumeration<SaslServerFactory>() { 568 public boolean hasMoreElements() { 569 return iter.hasNext(); 570 } 571 public SaslServerFactory nextElement() { 572 return (SaslServerFactory)iter.next(); 573 } 574 }; 575 } 576 577 private static Set<Object> getFactories(String serviceName) { 578 HashSet<Object> result = new HashSet<Object>(); 579 580 if ((serviceName == null) || (serviceName.length() == 0) || 581 (serviceName.endsWith("."))) { 582 return result; 583 } 584 585 586 Provider[] providers = Security.getProviders(); 587 HashSet<String> classes = new HashSet<String>(); 588 Object fac; 589 590 for (int i = 0; i < providers.length; i++) { 591 classes.clear(); 592 593 // Check the keys for each provider. 594 for (Enumeration<Object> e = providers[i].keys(); e.hasMoreElements(); ) { 595 String currentKey = (String)e.nextElement(); 596 if (currentKey.startsWith(serviceName)) { 597 // We should skip the currentKey if it contains a 598 // whitespace. The reason is: such an entry in the 599 // provider property contains attributes for the 600 // implementation of an algorithm. We are only interested 601 // in entries which lead to the implementation 602 // classes. 603 if (currentKey.indexOf(" ") < 0) { 604 String className = providers[i].getProperty(currentKey); 605 if (!classes.contains(className)) { 606 classes.add(className); 607 try { 608 fac = loadFactory(providers[i], className); 609 if (fac != null) { 610 result.add(fac); 611 } 612 }catch (Exception ignore) { 613 } 614 } 615 } 616 } 617 } 618 } 619 return Collections.unmodifiableSet(result); 620 } 621 }