/*
* 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;
}
}