/* * 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 * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.admin.ads; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.logging.Logger; import javax.naming.NameNotFoundException; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.naming.ldap.InitialLdapContext; import javax.naming.ldap.LdapName; import org.opends.admin.ads.util.ConnectionUtils; /** * The object of this class represent an OpenDS server. */ public class ServerDescriptor { private Map adsProperties = new HashMap(); private Set replicas = new HashSet(); private Map serverProperties = new HashMap(); private TopologyCacheException lastException; /** * Enumeration containing the different server properties that we can keep in * the ServerProperty object. */ public enum ServerProperty { /** * The associated value is a String. */ HOST_NAME, /** * The associated value is an ArrayList of Integer. */ LDAP_PORT, /** * The associated value is an ArrayList of Integer. */ LDAPS_PORT, /** * The associated value is an ArrayList of Boolean. */ LDAP_ENABLED, /** * The associated value is an ArrayList of Boolean. */ LDAPS_ENABLED, /** * The associated value is an ArrayList of Integer. */ JMX_PORT, /** * The associated value is an ArrayList of Integer. */ JMXS_PORT, /** * The associated value is an ArrayList of Boolean. */ JMX_ENABLED, /** * The associated value is an ArrayList of Boolean. */ JMXS_ENABLED, /** * The associated value is an Integer. */ REPLICATION_SERVER_PORT, /** * The associated value is a Boolean. */ IS_REPLICATION_SERVER, /** * The associated value is a Boolean. */ IS_REPLICATION_ENABLED, /** * List of servers specified in the Replication Server configuration. * This is a Set of String. */ EXTERNAL_REPLICATION_SERVERS, /** * The associated value is an Integer. */ REPLICATION_SERVER_ID } private static final Logger LOG = Logger.getLogger(ServerDescriptor.class.getName()); private ServerDescriptor() { } /** * Returns the replicas contained on the server. * @return the replicas contained on the server. */ public Set getReplicas() { Set copy = new HashSet(); copy.addAll(replicas); return copy; } /** * Sets the replicas contained on the server. * @param replicas the replicas contained on the server. */ public void setReplicas(Set replicas) { this.replicas.clear(); this.replicas.addAll(replicas); } /** * Returns a Map containing the ADS properties of the server. * @return a Map containing the ADS properties of the server. */ public Map getAdsProperties() { return adsProperties; } /** * Returns a Map containing the properties of the server. * @return a Map containing the properties of the server. */ public Map getServerProperties() { return serverProperties; } /** * Tells whether this server is registered in the ADS or not. * @return true if the server is registered in the ADS and * false otherwise. */ public boolean isRegistered() { return !adsProperties.isEmpty(); } /** * Tells whether this server is a replication server or not. * @return true if the server is a replication server and * false otherwise. */ public boolean isReplicationServer() { return Boolean.TRUE.equals( serverProperties.get(ServerProperty.IS_REPLICATION_SERVER)); } /** * Returns the String representation of this replication server based * on the information we have ("hostname":"replication port") and * null if this is not a replication server. * @return the String representation of this replication server based * on the information we have ("hostname":"replication port") and * null if this is not a replication server. */ public String getReplicationServerHostPort() { String hostPort = null; if (isReplicationServer()) { hostPort = getHostName().toLowerCase()+ ":" + getReplicationServerPort(); } return hostPort; } /** * Returns the replication server ID of this server and -1 if this is not a * replications server. * @return the replication server ID of this server and -1 if this is not a * replications server. */ public int getReplicationServerId() { int port = -1; if (isReplicationServer()) { port = (Integer)serverProperties.get( ServerProperty.REPLICATION_SERVER_ID); } return port; } /** * Returns the replication port of this server and -1 if this is not a * replications server. * @return the replication port of this server and -1 if this is not a * replications server. */ public int getReplicationServerPort() { int port = -1; if (isReplicationServer()) { port = (Integer)serverProperties.get( ServerProperty.REPLICATION_SERVER_PORT); } return port; } /** * Sets the ADS properties of the server. * @param adsProperties a Map containing the ADS properties of the server. */ public void setAdsProperties( Map adsProperties) { this.adsProperties = adsProperties; } /** * Returns the host name of the server. * @return the host name of the server. */ public String getHostName() { String host = (String)serverProperties.get(ServerProperty.HOST_NAME); if (host == null) { host = (String)adsProperties.get(ADSContext.ServerProperty.HOST_NAME); } return host; } /** * Returns a String of type host-name:port-number for the server. If * the provided securePreferred is set to true the port that will be used * (if LDAPS is enabled) will be the LDAPS port. * @param securePreferred whether to try to use the secure port as part * of the returning String or not. * @return a String of type host-name:port-number for the server. */ public String getHostPort(boolean securePreferred) { String host = getHostName(); int port = -1; if (!serverProperties.isEmpty()) { ArrayList s = (ArrayList)serverProperties.get( ServerProperty.LDAP_ENABLED); ArrayList p = (ArrayList)serverProperties.get( ServerProperty.LDAP_PORT); if (s != null) { for (int i=0; i 0) { buf.append(serverProperties.get(ServerProperty.HOST_NAME)); ServerProperty [] props = { ServerProperty.LDAP_PORT, ServerProperty.LDAPS_PORT, ServerProperty.LDAP_ENABLED, ServerProperty.LDAPS_ENABLED }; for (int i=0; i 0) { port = (Integer)p.iterator().next(); } } else { adsProperties.put(adsProps[i][0], "true"); } adsProperties.put(adsProps[i][1], String.valueOf(port)); } } adsProperties.put(ADSContext.ServerProperty.ID, getHostPort(true)); } /** * Creates a ServerDescriptor object based on some ADS properties provided. * @param adsProperties the ADS properties of the server. * @return a ServerDescriptor object that corresponds to the provided ADS * properties. */ public static ServerDescriptor createStandalone( Map adsProperties) { ServerDescriptor desc = new ServerDescriptor(); desc.setAdsProperties(adsProperties); return desc; } /** * Creates a ServerDescriptor object based on the configuration that we read * using the provided InitialLdapContext. * @param ctx the InitialLdapContext that will be used to read the * configuration of the server. * @return a ServerDescriptor object that corresponds to the read * configuration. * @throws NamingException if a problem occurred reading the server * configuration. */ public static ServerDescriptor createStandalone(InitialLdapContext ctx) throws NamingException { ServerDescriptor desc = new ServerDescriptor(); updateLdapConfiguration(desc, ctx); updateJmxConfiguration(desc, ctx); updateReplicas(desc, ctx); updateReplication(desc, ctx); desc.serverProperties.put(ServerProperty.HOST_NAME, ConnectionUtils.getHostName(ctx)); return desc; } private static void updateLdapConfiguration(ServerDescriptor desc, InitialLdapContext ctx) throws NamingException { SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); ctls.setReturningAttributes( new String[] { "ds-cfg-connection-handler-enabled", "ds-cfg-listen-address", "ds-cfg-listen-port", "ds-cfg-use-ssl", "objectclass" }); String filter = "(objectclass=ds-cfg-ldap-connection-handler)"; LdapName jndiName = new LdapName("cn=config"); NamingEnumeration listeners = ctx.search(jndiName, filter, ctls); ArrayList ldapPorts = new ArrayList(); ArrayList ldapsPorts = new ArrayList(); ArrayList ldapEnabled = new ArrayList(); ArrayList ldapsEnabled = new ArrayList(); desc.serverProperties.put(ServerProperty.LDAP_PORT, ldapPorts); desc.serverProperties.put(ServerProperty.LDAPS_PORT, ldapsPorts); desc.serverProperties.put(ServerProperty.LDAP_ENABLED, ldapEnabled); desc.serverProperties.put(ServerProperty.LDAPS_ENABLED, ldapsEnabled); while(listeners.hasMore()) { SearchResult sr = (SearchResult)listeners.next(); String port = getFirstValue(sr, "ds-cfg-listen-port"); boolean isSecure = "true".equalsIgnoreCase( getFirstValue(sr, "ds-cfg-use-ssl")); boolean enabled = "true".equalsIgnoreCase( getFirstValue(sr, "ds-cfg-connection-handler-enabled")); if (isSecure) { ldapsPorts.add(new Integer(port)); ldapsEnabled.add(enabled); } else { ldapPorts.add(new Integer(port)); ldapEnabled.add(enabled); } } } private static void updateJmxConfiguration(ServerDescriptor desc, InitialLdapContext ctx) throws NamingException { SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); ctls.setReturningAttributes( new String[] { "ds-cfg-connection-handler-enabled", "ds-cfg-listen-address", "ds-cfg-listen-port", "ds-cfg-use-ssl", "objectclass" }); String filter = "(objectclass=ds-cfg-jmx-connection-handler)"; LdapName jndiName = new LdapName("cn=config"); NamingEnumeration listeners = ctx.search(jndiName, filter, ctls); ArrayList jmxPorts = new ArrayList(); ArrayList jmxsPorts = new ArrayList(); ArrayList jmxEnabled = new ArrayList(); ArrayList jmxsEnabled = new ArrayList(); desc.serverProperties.put(ServerProperty.JMX_PORT, jmxPorts); desc.serverProperties.put(ServerProperty.JMXS_PORT, jmxsPorts); desc.serverProperties.put(ServerProperty.JMX_ENABLED, jmxEnabled); desc.serverProperties.put(ServerProperty.JMXS_ENABLED, jmxsEnabled); while(listeners.hasMore()) { SearchResult sr = (SearchResult)listeners.next(); String port = getFirstValue(sr, "ds-cfg-listen-port"); boolean isSecure = "true".equalsIgnoreCase( getFirstValue(sr, "ds-cfg-use-ssl")); boolean enabled = "true".equalsIgnoreCase( getFirstValue(sr, "ds-cfg-connection-handler-enabled")); if (isSecure) { jmxsPorts.add(new Integer(port)); jmxsEnabled.add(enabled); } else { jmxPorts.add(new Integer(port)); jmxEnabled.add(enabled); } } } private static void updateReplicas(ServerDescriptor desc, InitialLdapContext ctx) throws NamingException { SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); ctls.setReturningAttributes( new String[] { "ds-cfg-backend-base-dn", "ds-cfg-backend-id" }); String filter = "(objectclass=ds-cfg-backend)"; LdapName jndiName = new LdapName("cn=config"); NamingEnumeration databases = ctx.search(jndiName, filter, ctls); while(databases.hasMore()) { SearchResult sr = (SearchResult)databases.next(); String id = getFirstValue(sr, "ds-cfg-backend-id"); if (!isConfigBackend(id)) { Set baseDns = getValues(sr, "ds-cfg-backend-base-dn"); int nEntries = getEntryCount(ctx, id); Set replicas = desc.getReplicas(); for (String baseDn : baseDns) { SuffixDescriptor suffix = new SuffixDescriptor(); suffix.setDN(baseDn); ReplicaDescriptor replica = new ReplicaDescriptor(); replica.setServer(desc); replicas.add(replica); HashSet r = new HashSet(); r.add(replica); suffix.setReplicas(r); replica.setSuffix(suffix); if (baseDns.size() == 1) { replica.setEntries(nEntries); } else { /* Cannot know how many entries correspond to this replica */ replica.setEntries(-1); } } desc.setReplicas(replicas); } } } private static void updateReplication(ServerDescriptor desc, InitialLdapContext ctx) throws NamingException { boolean replicationEnabled = false; SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.OBJECT_SCOPE); ctls.setReturningAttributes( new String[] { "ds-cfg-synchronization-provider-enabled" }); String filter = "(objectclass=ds-cfg-synchronization-provider)"; LdapName jndiName = new LdapName( "cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config"); try { NamingEnumeration syncProviders = ctx.search(jndiName, filter, ctls); while(syncProviders.hasMore()) { SearchResult sr = (SearchResult)syncProviders.next(); if ("true".equalsIgnoreCase(getFirstValue(sr, "ds-cfg-synchronization-provider-enabled"))) { replicationEnabled = true; } } } catch (NameNotFoundException nse) { } desc.serverProperties.put(ServerProperty.IS_REPLICATION_ENABLED, replicationEnabled ? Boolean.TRUE : Boolean.FALSE); ctls = new SearchControls(); ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); ctls.setReturningAttributes( new String[] { "ds-cfg-replication-dn", "ds-cfg-replication-server", "ds-cfg-directory-server-id" }); filter = "(objectclass=ds-cfg-replication-domain-config)"; jndiName = new LdapName( "cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config"); try { NamingEnumeration syncProviders = ctx.search(jndiName, filter, ctls); while(syncProviders.hasMore()) { SearchResult sr = (SearchResult)syncProviders.next(); int id = Integer.parseInt( getFirstValue(sr, "ds-cfg-directory-server-id")); Set replicationServers = getValues(sr, "ds-cfg-replication-server"); Set dns = getValues(sr, "ds-cfg-replication-dn"); for (String dn : dns) { for (ReplicaDescriptor replica : desc.getReplicas()) { if (areDnsEqual(replica.getSuffix().getDN(), dn)) { replica.setReplicationId(id); // Keep the values of the replication servers in lower case // to make use of Sets as String simpler. LinkedHashSet repServers = new LinkedHashSet(); for (String s: replicationServers) { repServers.add(s.toLowerCase()); } replica.setReplicationServers(repServers); } } } } } catch (NameNotFoundException nse) { } ctls = new SearchControls(); ctls.setSearchScope(SearchControls.OBJECT_SCOPE); ctls.setReturningAttributes( new String[] { "ds-cfg-replication-server-port", "ds-cfg-replication-server", "ds-cfg-replication-server-id" }); filter = "(objectclass=ds-cfg-replication-server-config)"; jndiName = new LdapName("cn=Replication Server,cn=Multimaster "+ "Synchronization,cn=Synchronization Providers,cn=config"); desc.serverProperties.put(ServerProperty.IS_REPLICATION_SERVER, Boolean.FALSE); try { NamingEnumeration entries = ctx.search(jndiName, filter, ctls); while(entries.hasMore()) { SearchResult sr = (SearchResult)entries.next(); desc.serverProperties.put(ServerProperty.IS_REPLICATION_SERVER, Boolean.TRUE); String v = getFirstValue(sr, "ds-cfg-replication-server-port"); desc.serverProperties.put(ServerProperty.REPLICATION_SERVER_PORT, Integer.parseInt(v)); v = getFirstValue(sr, "ds-cfg-replication-server-id"); desc.serverProperties.put(ServerProperty.REPLICATION_SERVER_ID, Integer.parseInt(v)); Set values = getValues(sr, "ds-cfg-replication-server"); // Keep the values of the replication servers in lower case // to make use of Sets as String simpler. LinkedHashSet repServers = new LinkedHashSet(); for (String s: values) { repServers.add(s.toLowerCase()); } desc.serverProperties.put(ServerProperty.EXTERNAL_REPLICATION_SERVERS, repServers); } } catch (NameNotFoundException nse) { } } /** * Returns the number of entries in a given backend using the provided * InitialLdapContext. * @param ctx the InitialLdapContext to use to update the configuration. * @param backendID the id of the backend. * @return the number of entries in the backend. * @throws NamingException if there was an error. */ private static int getEntryCount(InitialLdapContext ctx, String backendID) throws NamingException { int nEntries = -1; String v = null; SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.ONELEVEL_SCOPE); ctls.setReturningAttributes( new String[] { "ds-backend-entry-count" }); String filter = "(ds-backend-id="+backendID+")"; LdapName jndiName = new LdapName("cn=monitor"); NamingEnumeration listeners = ctx.search(jndiName, filter, ctls); while(listeners.hasMore()) { SearchResult sr = (SearchResult)listeners.next(); v = getFirstValue(sr, "ds-backend-entry-count"); } try { nEntries = Integer.parseInt(v); } catch (Exception ex) { } return nEntries; } /* * The following 2 methods are convenience methods to retrieve String values * from an entry. */ private static String getFirstValue(SearchResult entry, String attrName) throws NamingException { return ConnectionUtils.getFirstValue(entry, attrName); } private static Set getValues(SearchResult entry, String attrName) throws NamingException { return ConnectionUtils.getValues(entry, attrName); } /** * An convenience method to know if the provided ID corresponds to a * configuration backend or not. * @param id the backend ID to analyze * @return true if the the id corresponds to a configuration * backend and false otherwise. */ private static boolean isConfigBackend(String id) { return "tasks".equalsIgnoreCase(id) || "schema".equalsIgnoreCase(id) || "config".equalsIgnoreCase(id) || "monitor".equalsIgnoreCase(id) || "backup".equalsIgnoreCase(id) || "ads-truststore".equalsIgnoreCase(id); } /** * Returns true if the the provided strings represent the same * DN and false otherwise. * @param dn1 the first dn to compare. * @param dn2 the second dn to compare. * @return true if the the provided strings represent the same * DN and false otherwise. */ private static boolean areDnsEqual(String dn1, String dn2) { boolean areDnsEqual = false; try { LdapName name1 = new LdapName(dn1); LdapName name2 = new LdapName(dn2); areDnsEqual = name1.equals(name2); } catch (Exception ex) { } return areDnsEqual; } }