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 }