001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2008 Sun Microsystems, Inc. 026 */ 027 028 package org.opends.admin.ads.util; 029 030 import java.util.LinkedHashSet; 031 import java.util.Map; 032 import java.util.logging.Level; 033 import java.util.logging.Logger; 034 035 import javax.naming.AuthenticationException; 036 import javax.naming.NamingException; 037 import javax.naming.NoPermissionException; 038 import javax.naming.TimeLimitExceededException; 039 import javax.naming.ldap.InitialLdapContext; 040 import javax.naming.ldap.LdapName; 041 042 import org.opends.admin.ads.ADSContext; 043 import org.opends.admin.ads.ServerDescriptor; 044 import org.opends.admin.ads.TopologyCacheException; 045 import org.opends.admin.ads.TopologyCacheFilter; 046 import org.opends.admin.ads.ADSContext.ServerProperty; 047 048 /** 049 * Class used to load the configuration of a server. Basically the code 050 * uses some provided properties and authentication information to connect 051 * to the server and then generate a ServerDescriptor object based on the 052 * read configuration. 053 */ 054 public class ServerLoader extends Thread 055 { 056 private Map<ServerProperty,Object> serverProperties; 057 private boolean isOver; 058 private boolean isInterrupted; 059 private String lastLdapUrl; 060 private TopologyCacheException lastException; 061 private ServerDescriptor serverDescriptor; 062 private ApplicationTrustManager trustManager; 063 private String dn; 064 private String pwd; 065 private LinkedHashSet<PreferredConnection> preferredLDAPURLs; 066 private TopologyCacheFilter filter; 067 068 private static final Logger LOG = 069 Logger.getLogger(ServerLoader.class.getName()); 070 071 /** 072 * Constructor. 073 * @param serverProperties the server properties of the server we want to 074 * load. 075 * @param dn the DN that we must use to bind to the server. 076 * @param pwd the password that we must use to bind to the server. 077 * @param trustManager the ApplicationTrustManager to be used when we try 078 * to connect to the server. 079 * @param preferredLDAPURLs the list of preferred LDAP URLs that we want 080 * to use to connect to the server. They will be used only if they correspond 081 * to the URLs that we found in the the server properties. 082 * @param filter the topology cache filter to be used. This can be used not 083 * to retrieve all the information. 084 */ 085 public ServerLoader(Map<ServerProperty,Object> serverProperties, 086 String dn, String pwd, ApplicationTrustManager trustManager, 087 LinkedHashSet<PreferredConnection> preferredLDAPURLs, 088 TopologyCacheFilter filter) 089 { 090 this.serverProperties = serverProperties; 091 this.dn = dn; 092 this.pwd = pwd; 093 this.trustManager = trustManager; 094 this.preferredLDAPURLs = 095 new LinkedHashSet<PreferredConnection>(preferredLDAPURLs); 096 this.filter = filter; 097 } 098 099 /** 100 * Returns the ServerDescriptor that could be retrieved. 101 * @return the ServerDescriptor that could be retrieved. 102 */ 103 public ServerDescriptor getServerDescriptor() 104 { 105 if (serverDescriptor == null) 106 { 107 serverDescriptor = ServerDescriptor.createStandalone(serverProperties); 108 } 109 serverDescriptor.setLastException(lastException); 110 return serverDescriptor; 111 } 112 113 /** 114 * Returns the last exception that occurred while trying to generate 115 * the ServerDescriptor object. 116 * @return the last exception that occurred while trying to generate 117 * the ServerDescriptor object. 118 */ 119 public TopologyCacheException getLastException() 120 { 121 return lastException; 122 } 123 124 /** 125 * {@inheritDoc} 126 */ 127 public void interrupt() 128 { 129 if (!isOver) 130 { 131 isInterrupted = true; 132 String ldapUrl = getLastLdapUrl(); 133 if (ldapUrl == null) 134 { 135 LinkedHashSet<PreferredConnection> urls = getLDAPURLsByPreference(); 136 if (!urls.isEmpty()) 137 { 138 ldapUrl = urls.iterator().next().getLDAPURL(); 139 } 140 } 141 lastException = new TopologyCacheException( 142 TopologyCacheException.Type.TIMEOUT, 143 new TimeLimitExceededException("Timeout reading server: "+ldapUrl), 144 trustManager, ldapUrl); 145 LOG.log(Level.WARNING, "Timeout reading server: "+ldapUrl); 146 } 147 super.interrupt(); 148 } 149 150 /** 151 * The method where we try to generate the ServerDescriptor object. 152 */ 153 public void run() 154 { 155 lastException = null; 156 InitialLdapContext ctx = null; 157 try 158 { 159 ctx = createContext(); 160 serverDescriptor = ServerDescriptor.createStandalone(ctx, filter); 161 serverDescriptor.setAdsProperties(serverProperties); 162 } 163 catch (NoPermissionException npe) 164 { 165 LOG.log(Level.WARNING, 166 "Permissions error reading server: "+getLastLdapUrl(), npe); 167 if (!isAdministratorDn()) 168 { 169 lastException = new TopologyCacheException( 170 TopologyCacheException.Type.NOT_GLOBAL_ADMINISTRATOR, npe, 171 trustManager, getLastLdapUrl()); 172 } 173 else 174 { 175 lastException = 176 new TopologyCacheException( 177 TopologyCacheException.Type.NO_PERMISSIONS, npe, 178 trustManager, getLastLdapUrl()); 179 } 180 } 181 catch (AuthenticationException ae) 182 { 183 LOG.log(Level.WARNING, 184 "Authentication exception: "+getLastLdapUrl(), ae); 185 if (!isAdministratorDn()) 186 { 187 lastException = new TopologyCacheException( 188 TopologyCacheException.Type.NOT_GLOBAL_ADMINISTRATOR, ae, 189 trustManager, getLastLdapUrl()); 190 } 191 else 192 { 193 lastException = 194 new TopologyCacheException( 195 TopologyCacheException.Type.GENERIC_READING_SERVER, ae, 196 trustManager, getLastLdapUrl()); 197 } 198 } 199 catch (NamingException ne) 200 { 201 LOG.log(Level.WARNING, 202 "NamingException error reading server: "+getLastLdapUrl(), ne); 203 if (ctx == null) 204 { 205 lastException = 206 new TopologyCacheException( 207 TopologyCacheException.Type.GENERIC_CREATING_CONNECTION, ne, 208 trustManager, getLastLdapUrl()); 209 } 210 else 211 { 212 lastException = 213 new TopologyCacheException( 214 TopologyCacheException.Type.GENERIC_READING_SERVER, ne, 215 trustManager, getLastLdapUrl()); 216 } 217 } 218 catch (Throwable t) 219 { 220 if (!isInterrupted) 221 { 222 LOG.log(Level.WARNING, 223 "Generic error reading server: "+getLastLdapUrl(), t); 224 LOG.log(Level.WARNING, "server Properties: "+serverProperties); 225 lastException = 226 new TopologyCacheException(TopologyCacheException.Type.BUG, t); 227 } 228 } 229 finally 230 { 231 isOver = true; 232 try 233 { 234 if (ctx != null) 235 { 236 ctx.close(); 237 } 238 } 239 catch (Throwable t) 240 { 241 } 242 } 243 } 244 245 /** 246 * Create an InitialLdapContext based in the provide server properties and 247 * authentication data provided in the constructor. 248 * @return an InitialLdapContext based in the provide server properties and 249 * authentication data provided in the constructor. 250 * @throws NamingException if an error occurred while creating the 251 * InitialLdapContext. 252 */ 253 public InitialLdapContext createContext() throws NamingException 254 { 255 InitialLdapContext ctx = null; 256 if (trustManager != null) 257 { 258 trustManager.resetLastRefusedItems(); 259 260 String host = (String)serverProperties.get(ServerProperty.HOST_NAME); 261 trustManager.setHost(host); 262 } 263 264 /* Try to connect to the server in a certain order of preference. If an 265 * URL fails, we will try with the others. 266 */ 267 LinkedHashSet<PreferredConnection> conns = getLDAPURLsByPreference(); 268 269 for (PreferredConnection connection : conns) 270 { 271 if (ctx == null) 272 { 273 lastLdapUrl = connection.getLDAPURL(); 274 switch (connection.getType()) 275 { 276 case LDAPS: 277 ctx = ConnectionUtils.createLdapsContext(lastLdapUrl, dn, pwd, 278 ConnectionUtils.getDefaultLDAPTimeout(), null, trustManager, 279 null); 280 break; 281 case START_TLS: 282 ctx = ConnectionUtils.createStartTLSContext(lastLdapUrl, dn, pwd, 283 ConnectionUtils.getDefaultLDAPTimeout(), null, trustManager, 284 null, null); 285 break; 286 default: 287 ctx = ConnectionUtils.createLdapContext(lastLdapUrl, dn, pwd, 288 ConnectionUtils.getDefaultLDAPTimeout(), null); 289 } 290 } 291 } 292 return ctx; 293 } 294 295 /** 296 * Returns the last LDAP URL to which we tried to connect. 297 * @return the last LDAP URL to which we tried to connect. 298 */ 299 private String getLastLdapUrl() 300 { 301 return lastLdapUrl; 302 } 303 304 /** 305 * Returns the non-secure LDAP URL for the given server properties. It 306 * returns NULL if according to the server properties no non-secure LDAP URL 307 * can be generated (LDAP disabled or port not defined). 308 * @param serverProperties the server properties to be used to generate 309 * the non-secure LDAP URL. 310 * @return the non-secure LDAP URL for the given server properties. 311 */ 312 private String getLdapUrl(Map<ServerProperty,Object> serverProperties) 313 { 314 String ldapUrl = null; 315 Object v = serverProperties.get(ServerProperty.LDAP_ENABLED); 316 boolean ldapEnabled = (v != null) && "true".equalsIgnoreCase(v.toString()); 317 if (ldapEnabled) 318 { 319 ldapUrl = "ldap://"+getHostNameForLdapUrl(serverProperties)+":"+ 320 serverProperties.get(ServerProperty.LDAP_PORT); 321 } 322 return ldapUrl; 323 } 324 325 /** 326 * Returns the StartTLS LDAP URL for the given server properties. It 327 * returns NULL if according to the server properties no StartTLS LDAP URL 328 * can be generated (StartTLS disabled or port not defined). 329 * @param serverProperties the server properties to be used to generate 330 * the StartTLS LDAP URL. 331 * @return the StartTLS LDAP URL for the given server properties. 332 */ 333 private String getStartTlsLdapUrl(Map<ServerProperty,Object> serverProperties) 334 { 335 String ldapUrl = null; 336 Object v = serverProperties.get(ServerProperty.LDAP_ENABLED); 337 boolean ldapEnabled = (v != null) && "true".equalsIgnoreCase(v.toString()); 338 v = serverProperties.get(ServerProperty.STARTTLS_ENABLED); 339 boolean startTLSEnabled = (v != null) && 340 "true".equalsIgnoreCase(v.toString()); 341 if (ldapEnabled && startTLSEnabled) 342 { 343 ldapUrl = "ldap://"+getHostNameForLdapUrl(serverProperties)+":"+ 344 serverProperties.get(ServerProperty.LDAP_PORT); 345 } 346 return ldapUrl; 347 } 348 349 /** 350 * Returns the LDAPs URL for the given server properties. It 351 * returns NULL if according to the server properties no LDAPS URL 352 * can be generated (LDAPS disabled or port not defined). 353 * @param serverProperties the server properties to be used to generate 354 * the LDAPS URL. 355 * @return the LDAPS URL for the given server properties. 356 */ 357 private String getLdapsUrl(Map<ServerProperty,Object> serverProperties) 358 { 359 String ldapsUrl = null; 360 Object v = serverProperties.get(ServerProperty.LDAPS_ENABLED); 361 boolean ldapsEnabled = (v != null) && "true".equalsIgnoreCase(v.toString()); 362 if (ldapsEnabled) 363 { 364 ldapsUrl = "ldaps://"+getHostNameForLdapUrl(serverProperties)+":"+ 365 serverProperties.get(ServerProperty.LDAPS_PORT); 366 } 367 return ldapsUrl; 368 } 369 370 /** 371 * Returns the host name to be used to generate an LDAP URL based on the 372 * contents of the provided server properties. 373 * @param serverProperties the server properties. 374 * @return the host name to be used to generate an LDAP URL based on the 375 * contents of the provided server properties. 376 */ 377 private String getHostNameForLdapUrl( 378 Map<ServerProperty,Object> serverProperties) 379 { 380 String host = (String)serverProperties.get(ServerProperty.HOST_NAME); 381 return ConnectionUtils.getHostNameForLdapUrl(host); 382 } 383 384 /** 385 * Returns whether the DN provided in the constructor is a Global 386 * Administrator DN or not. 387 * @return <CODE>true</CODE> if the DN provided in the constructor is a Global 388 * Administrator DN and <CODE>false</CODE> otherwise. 389 */ 390 private boolean isAdministratorDn() 391 { 392 boolean isAdministratorDn = false; 393 try 394 { 395 LdapName theDn = new LdapName(dn); 396 LdapName containerDn = 397 new LdapName(ADSContext.getAdministratorContainerDN()); 398 isAdministratorDn = theDn.startsWith(containerDn); 399 } 400 catch (Throwable t) 401 { 402 LOG.log(Level.WARNING, "Error parsing authentication DNs.", t); 403 } 404 return isAdministratorDn; 405 } 406 407 /** 408 * Returns the list of LDAP URLs that can be used to connect to the server. 409 * They are ordered so that the first URL is the preferred URL to be used. 410 * @return the list of LDAP URLs that can be used to connect to the server. 411 * They are ordered so that the first URL is the preferred URL to be used. 412 */ 413 private LinkedHashSet<PreferredConnection> getLDAPURLsByPreference() 414 { 415 LinkedHashSet<PreferredConnection> ldapUrls = 416 new LinkedHashSet<PreferredConnection>(); 417 418 String ldapsUrl = getLdapsUrl(serverProperties); 419 String startTLSUrl = getStartTlsLdapUrl(serverProperties); 420 String ldapUrl = getLdapUrl(serverProperties); 421 422 /** 423 * Check the preferred connections passed in the constructor. 424 */ 425 for (PreferredConnection connection : preferredLDAPURLs) 426 { 427 String url = connection.getLDAPURL(); 428 if (url.equalsIgnoreCase(ldapsUrl) && 429 connection.getType() == PreferredConnection.Type.LDAPS) 430 { 431 ldapUrls.add(connection); 432 } 433 else if (url.equalsIgnoreCase(startTLSUrl) && 434 connection.getType() == PreferredConnection.Type.START_TLS) 435 { 436 ldapUrls.add(connection); 437 } 438 else if (url.equalsIgnoreCase(ldapUrl) && 439 connection.getType() == PreferredConnection.Type.LDAP) 440 { 441 ldapUrls.add(connection); 442 } 443 } 444 445 if (ldapsUrl != null) 446 { 447 ldapUrls.add( 448 new PreferredConnection(ldapsUrl, PreferredConnection.Type.LDAPS)); 449 } 450 if (startTLSUrl != null) 451 { 452 ldapUrls.add(new PreferredConnection(startTLSUrl, 453 PreferredConnection.Type.START_TLS)); 454 } 455 if (ldapUrl != null) 456 { 457 ldapUrls.add(new PreferredConnection(ldapUrl, 458 PreferredConnection.Type.LDAP)); 459 } 460 return ldapUrls; 461 } 462 }