/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008-2010 Sun Microsystems, Inc. */ package org.opends.admin.ads.util; import java.util.LinkedHashSet; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.naming.AuthenticationException; import javax.naming.NamingException; import javax.naming.NoPermissionException; import javax.naming.TimeLimitExceededException; import javax.naming.ldap.InitialLdapContext; import javax.naming.ldap.LdapName; import org.opends.admin.ads.ADSContext; import org.opends.admin.ads.ServerDescriptor; import org.opends.admin.ads.TopologyCacheException; import org.opends.admin.ads.TopologyCacheFilter; import org.opends.admin.ads.ADSContext.ServerProperty; /** * Class used to load the configuration of a server. Basically the code * uses some provided properties and authentication information to connect * to the server and then generate a ServerDescriptor object based on the * read configuration. */ public class ServerLoader extends Thread { private Map<ServerProperty,Object> serverProperties; private boolean isOver; private boolean isInterrupted; private String lastLdapUrl; private TopologyCacheException lastException; private ServerDescriptor serverDescriptor; private ApplicationTrustManager trustManager; private int timeout; private String dn; private String pwd; private LinkedHashSet<PreferredConnection> preferredLDAPURLs; private TopologyCacheFilter filter; private static final Logger LOG = Logger.getLogger(ServerLoader.class.getName()); /** * Constructor. * @param serverProperties the server properties of the server we want to * load. * @param dn the DN that we must use to bind to the server. * @param pwd the password that we must use to bind to the server. * @param trustManager the ApplicationTrustManager to be used when we try * to connect to the server. * @param timeout the timeout to establish the connection in milliseconds. * Use {@code 0} to express no timeout. * @param preferredLDAPURLs the list of preferred LDAP URLs that we want * to use to connect to the server. They will be used only if they correspond * to the URLs that we found in the the server properties. * @param filter the topology cache filter to be used. This can be used not * to retrieve all the information. */ public ServerLoader(Map<ServerProperty,Object> serverProperties, String dn, String pwd, ApplicationTrustManager trustManager, int timeout, LinkedHashSet<PreferredConnection> preferredLDAPURLs, TopologyCacheFilter filter) { this.serverProperties = serverProperties; this.dn = dn; this.pwd = pwd; this.trustManager = trustManager; this.timeout = timeout; this.preferredLDAPURLs = new LinkedHashSet<PreferredConnection>(preferredLDAPURLs); this.filter = filter; } /** * Returns the ServerDescriptor that could be retrieved. * @return the ServerDescriptor that could be retrieved. */ public ServerDescriptor getServerDescriptor() { if (serverDescriptor == null) { serverDescriptor = ServerDescriptor.createStandalone(serverProperties); } serverDescriptor.setLastException(lastException); return serverDescriptor; } /** * Returns the last exception that occurred while trying to generate * the ServerDescriptor object. * @return the last exception that occurred while trying to generate * the ServerDescriptor object. */ public TopologyCacheException getLastException() { return lastException; } /** * {@inheritDoc} */ public void interrupt() { if (!isOver) { isInterrupted = true; String ldapUrl = getLastLdapUrl(); if (ldapUrl == null) { LinkedHashSet<PreferredConnection> urls = getLDAPURLsByPreference(); if (!urls.isEmpty()) { ldapUrl = urls.iterator().next().getLDAPURL(); } } lastException = new TopologyCacheException( TopologyCacheException.Type.TIMEOUT, new TimeLimitExceededException("Timeout reading server: "+ldapUrl), trustManager, ldapUrl); LOG.log(Level.WARNING, "Timeout reading server: "+ldapUrl); } super.interrupt(); } /** * The method where we try to generate the ServerDescriptor object. */ public void run() { lastException = null; InitialLdapContext ctx = null; try { ctx = createContext(); serverDescriptor = ServerDescriptor.createStandalone(ctx, filter); serverDescriptor.setAdsProperties(serverProperties); serverDescriptor.updateAdsPropertiesWithServerProperties(); } catch (NoPermissionException npe) { LOG.log(Level.WARNING, "Permissions error reading server: "+getLastLdapUrl(), npe); if (!isAdministratorDn()) { lastException = new TopologyCacheException( TopologyCacheException.Type.NOT_GLOBAL_ADMINISTRATOR, npe, trustManager, getLastLdapUrl()); } else { lastException = new TopologyCacheException( TopologyCacheException.Type.NO_PERMISSIONS, npe, trustManager, getLastLdapUrl()); } } catch (AuthenticationException ae) { LOG.log(Level.WARNING, "Authentication exception: "+getLastLdapUrl(), ae); if (!isAdministratorDn()) { lastException = new TopologyCacheException( TopologyCacheException.Type.NOT_GLOBAL_ADMINISTRATOR, ae, trustManager, getLastLdapUrl()); } else { lastException = new TopologyCacheException( TopologyCacheException.Type.GENERIC_READING_SERVER, ae, trustManager, getLastLdapUrl()); } } catch (NamingException ne) { LOG.log(Level.WARNING, "NamingException error reading server: "+getLastLdapUrl(), ne); if (ctx == null) { lastException = new TopologyCacheException( TopologyCacheException.Type.GENERIC_CREATING_CONNECTION, ne, trustManager, getLastLdapUrl()); } else { lastException = new TopologyCacheException( TopologyCacheException.Type.GENERIC_READING_SERVER, ne, trustManager, getLastLdapUrl()); } } catch (Throwable t) { if (!isInterrupted) { LOG.log(Level.WARNING, "Generic error reading server: "+getLastLdapUrl(), t); LOG.log(Level.WARNING, "server Properties: "+serverProperties); lastException = new TopologyCacheException(TopologyCacheException.Type.BUG, t); } } finally { isOver = true; try { if (ctx != null) { ctx.close(); } } catch (Throwable t) { } } } /** * Create an InitialLdapContext based in the provide server properties and * authentication data provided in the constructor. * @return an InitialLdapContext based in the provide server properties and * authentication data provided in the constructor. * @throws NamingException if an error occurred while creating the * InitialLdapContext. */ public InitialLdapContext createContext() throws NamingException { InitialLdapContext ctx = null; if (trustManager != null) { trustManager.resetLastRefusedItems(); String host = (String)serverProperties.get(ServerProperty.HOST_NAME); trustManager.setHost(host); } /* Try to connect to the server in a certain order of preference. If an * URL fails, we will try with the others. */ LinkedHashSet<PreferredConnection> conns = getLDAPURLsByPreference(); for (PreferredConnection connection : conns) { if (ctx == null) { lastLdapUrl = connection.getLDAPURL(); switch (connection.getType()) { case LDAPS: ctx = ConnectionUtils.createLdapsContext(lastLdapUrl, dn, pwd, timeout, null, trustManager, null); break; case START_TLS: ctx = ConnectionUtils.createStartTLSContext(lastLdapUrl, dn, pwd, timeout, null, trustManager, null, null); break; default: ctx = ConnectionUtils.createLdapContext(lastLdapUrl, dn, pwd, timeout, null); } } } return ctx; } /** * Returns the last LDAP URL to which we tried to connect. * @return the last LDAP URL to which we tried to connect. */ private String getLastLdapUrl() { return lastLdapUrl; } /** * Returns the non-secure LDAP URL for the given server properties. It * returns NULL if according to the server properties no non-secure LDAP URL * can be generated (LDAP disabled or port not defined). * @param serverProperties the server properties to be used to generate * the non-secure LDAP URL. * @return the non-secure LDAP URL for the given server properties. */ private String getLdapUrl(Map<ServerProperty,Object> serverProperties) { String ldapUrl = null; Object v = serverProperties.get(ServerProperty.LDAP_ENABLED); boolean ldapEnabled = (v != null) && "true".equalsIgnoreCase(v.toString()); if (ldapEnabled) { ldapUrl = "ldap://"+getHostNameForLdapUrl(serverProperties)+":"+ serverProperties.get(ServerProperty.LDAP_PORT); } return ldapUrl; } /** * Returns the StartTLS LDAP URL for the given server properties. It * returns NULL if according to the server properties no StartTLS LDAP URL * can be generated (StartTLS disabled or port not defined). * @param serverProperties the server properties to be used to generate * the StartTLS LDAP URL. * @return the StartTLS LDAP URL for the given server properties. */ private String getStartTlsLdapUrl(Map<ServerProperty,Object> serverProperties) { String ldapUrl = null; Object v = serverProperties.get(ServerProperty.LDAP_ENABLED); boolean ldapEnabled = (v != null) && "true".equalsIgnoreCase(v.toString()); v = serverProperties.get(ServerProperty.STARTTLS_ENABLED); boolean startTLSEnabled = (v != null) && "true".equalsIgnoreCase(v.toString()); if (ldapEnabled && startTLSEnabled) { ldapUrl = "ldap://"+getHostNameForLdapUrl(serverProperties)+":"+ serverProperties.get(ServerProperty.LDAP_PORT); } return ldapUrl; } /** * Returns the LDAPs URL for the given server properties. It * returns NULL if according to the server properties no LDAPS URL * can be generated (LDAPS disabled or port not defined). * @param serverProperties the server properties to be used to generate * the LDAPS URL. * @return the LDAPS URL for the given server properties. */ private String getLdapsUrl(Map<ServerProperty,Object> serverProperties) { String ldapsUrl = null; Object v = serverProperties.get(ServerProperty.LDAPS_ENABLED); boolean ldapsEnabled = (v != null) && "true".equalsIgnoreCase(v.toString()); if (ldapsEnabled) { ldapsUrl = "ldaps://"+getHostNameForLdapUrl(serverProperties)+":"+ serverProperties.get(ServerProperty.LDAPS_PORT); } return ldapsUrl; } /** * Returns the administration connector URL for the given server properties. * It returns NULL if according to the server properties no administration * connector URL can be generated. * @param serverProperties the server properties to be used to generate * the administration connector URL. * @return the administration connector URL for the given server properties. */ private String getAdminConnectorUrl( Map<ServerProperty,Object> serverProperties) { String adminUrl = null; boolean portDefined; Object v = serverProperties.get(ServerProperty.ADMIN_ENABLED); if ((v != null) && "true".equalsIgnoreCase(String.valueOf(v))) { v = serverProperties.get(ServerProperty.ADMIN_PORT); portDefined = v != null; } else { portDefined = false; } if (portDefined) { adminUrl = "ldaps://"+getHostNameForLdapUrl(serverProperties)+":"+ serverProperties.get(ServerProperty.ADMIN_PORT); } return adminUrl; } /** * Returns the host name to be used to generate an LDAP URL based on the * contents of the provided server properties. * @param serverProperties the server properties. * @return the host name to be used to generate an LDAP URL based on the * contents of the provided server properties. */ private String getHostNameForLdapUrl( Map<ServerProperty,Object> serverProperties) { String host = (String)serverProperties.get(ServerProperty.HOST_NAME); return ConnectionUtils.getHostNameForLdapUrl(host); } /** * Returns whether the DN provided in the constructor is a Global * Administrator DN or not. * @return <CODE>true</CODE> if the DN provided in the constructor is a Global * Administrator DN and <CODE>false</CODE> otherwise. */ private boolean isAdministratorDn() { boolean isAdministratorDn = false; try { LdapName theDn = new LdapName(dn); LdapName containerDn = new LdapName(ADSContext.getAdministratorContainerDN()); isAdministratorDn = theDn.startsWith(containerDn); } catch (Throwable t) { LOG.log(Level.WARNING, "Error parsing authentication DNs.", t); } return isAdministratorDn; } /** * Returns the list of LDAP URLs that can be used to connect to the server. * They are ordered so that the first URL is the preferred URL to be used. * @return the list of LDAP URLs that can be used to connect to the server. * They are ordered so that the first URL is the preferred URL to be used. */ private LinkedHashSet<PreferredConnection> getLDAPURLsByPreference() { LinkedHashSet<PreferredConnection> ldapUrls = new LinkedHashSet<PreferredConnection>(); String adminConnectorUrl = getAdminConnectorUrl(serverProperties); String ldapsUrl = getLdapsUrl(serverProperties); String startTLSUrl = getStartTlsLdapUrl(serverProperties); String ldapUrl = getLdapUrl(serverProperties); /** * Check the preferred connections passed in the constructor. */ for (PreferredConnection connection : preferredLDAPURLs) { String url = connection.getLDAPURL(); if (url.equalsIgnoreCase(adminConnectorUrl)) { ldapUrls.add(connection); } else if (url.equalsIgnoreCase(ldapsUrl) && connection.getType() == PreferredConnection.Type.LDAPS) { ldapUrls.add(connection); } else if (url.equalsIgnoreCase(startTLSUrl) && connection.getType() == PreferredConnection.Type.START_TLS) { ldapUrls.add(connection); } else if (url.equalsIgnoreCase(ldapUrl) && connection.getType() == PreferredConnection.Type.LDAP) { ldapUrls.add(connection); } } if (adminConnectorUrl != null) { ldapUrls.add( new PreferredConnection(adminConnectorUrl, PreferredConnection.Type.LDAPS)); } if (ldapsUrl != null) { ldapUrls.add( new PreferredConnection(ldapsUrl, PreferredConnection.Type.LDAPS)); } if (startTLSUrl != null) { ldapUrls.add(new PreferredConnection(startTLSUrl, PreferredConnection.Type.START_TLS)); } if (ldapUrl != null) { ldapUrls.add(new PreferredConnection(ldapUrl, PreferredConnection.Type.LDAP)); } return ldapUrls; } }