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; 029 030 import java.util.Date; 031 import java.util.HashMap; 032 import java.util.HashSet; 033 import java.util.Iterator; 034 import java.util.LinkedHashSet; 035 import java.util.Map; 036 import java.util.Set; 037 import java.util.logging.Level; 038 import java.util.logging.Logger; 039 040 import javax.naming.ldap.LdapName; 041 042 import org.opends.admin.ads.ADSContext.ServerProperty; 043 import org.opends.admin.ads.util.ApplicationTrustManager; 044 import org.opends.admin.ads.util.ConnectionUtils; 045 import org.opends.admin.ads.util.PreferredConnection; 046 import org.opends.admin.ads.util.ServerLoader; 047 048 /** 049 * This class allows to read the configuration of the different servers that 050 * are registered in a given ADS server. It provides a read only view of the 051 * configuration of the servers and of the replication topologies that might 052 * be configured between them. 053 */ 054 public class TopologyCache 055 { 056 private ADSContext adsContext; 057 private ApplicationTrustManager trustManager; 058 private String dn; 059 private String pwd; 060 private Set<ServerDescriptor> servers = new HashSet<ServerDescriptor>(); 061 private Set<SuffixDescriptor> suffixes = new HashSet<SuffixDescriptor>(); 062 private LinkedHashSet<PreferredConnection> preferredConnections = 063 new LinkedHashSet<PreferredConnection>(); 064 private TopologyCacheFilter filter = new TopologyCacheFilter(); 065 066 private final boolean isMultiThreaded = true; 067 private final static int MULTITHREAD_TIMEOUT = 90 * 1000; 068 069 private static final Logger LOG = 070 Logger.getLogger(TopologyCache.class.getName()); 071 072 073 /** 074 * Constructor of the TopologyCache. 075 * @param adsContext the adsContext to the ADS registry. 076 * @param trustManager the ApplicationTrustManager that must be used to trust 077 * certificates when we create connections to the registered servers to read 078 * their configuration. 079 */ 080 public TopologyCache(ADSContext adsContext, 081 ApplicationTrustManager trustManager) 082 { 083 this.adsContext = adsContext; 084 this.trustManager = trustManager; 085 dn = ConnectionUtils.getBindDN(adsContext.getDirContext()); 086 pwd = ConnectionUtils.getBindPassword(adsContext.getDirContext()); 087 } 088 089 /** 090 * Reads the configuration of the registered servers. 091 * @throws TopologyCacheException if there is an issue reading the 092 * configuration of the registered servers. 093 */ 094 public void reloadTopology() throws TopologyCacheException 095 { 096 suffixes.clear(); 097 servers.clear(); 098 try 099 { 100 Set<Map<ServerProperty,Object>> adsServers = 101 adsContext.readServerRegistry(); 102 103 Set<ServerLoader> threadSet = new HashSet<ServerLoader>(); 104 for (Map<ServerProperty,Object> serverProperties : adsServers) 105 { 106 ServerLoader t = getServerLoader(serverProperties); 107 if (isMultiThreaded) 108 { 109 t.start(); 110 threadSet.add(t); 111 } 112 else 113 { 114 t.run(); 115 } 116 } 117 if (isMultiThreaded) 118 { 119 joinThreadSet(threadSet); 120 } 121 /* Try to consolidate things (even if the data is not complete). */ 122 123 HashMap<LdapName, Set<SuffixDescriptor>> hmSuffixes = 124 new HashMap<LdapName, Set<SuffixDescriptor>>(); 125 for (ServerLoader loader : threadSet) 126 { 127 ServerDescriptor descriptor = loader.getServerDescriptor(); 128 for (ReplicaDescriptor replica : descriptor.getReplicas()) 129 { 130 LOG.log(Level.INFO, "Handling replica with dn: "+ 131 replica.getSuffix().getDN()); 132 133 boolean suffixFound = false; 134 LdapName dn = new LdapName(replica.getSuffix().getDN()); 135 Set<SuffixDescriptor> sufs = hmSuffixes.get(dn); 136 if (sufs != null) 137 { 138 Iterator<SuffixDescriptor> it = sufs.iterator(); 139 while (it.hasNext() && !suffixFound) 140 { 141 SuffixDescriptor suffix = it.next(); 142 Iterator<String> it2 = suffix.getReplicationServers().iterator(); 143 while (it2.hasNext() && !suffixFound) 144 { 145 if (replica.getReplicationServers().contains(it2.next())) 146 { 147 suffixFound = true; 148 Set<ReplicaDescriptor> replicas = suffix.getReplicas(); 149 replicas.add(replica); 150 suffix.setReplicas(replicas); 151 replica.setSuffix(suffix); 152 } 153 } 154 } 155 } 156 if (!suffixFound) 157 { 158 if (sufs == null) 159 { 160 sufs = new HashSet<SuffixDescriptor>(); 161 hmSuffixes.put(dn, sufs); 162 } 163 sufs.add(replica.getSuffix()); 164 suffixes.add(replica.getSuffix()); 165 } 166 } 167 servers.add(descriptor); 168 } 169 } 170 catch (ADSContextException ade) 171 { 172 throw new TopologyCacheException(ade); 173 } 174 catch (Throwable t) 175 { 176 throw new TopologyCacheException(TopologyCacheException.Type.BUG, t); 177 } 178 } 179 180 /** 181 * Sets the list of LDAP URLs and connection type that are preferred to be 182 * used to connect to the servers. When we have a server to which we can 183 * connect using a URL on the list we will try to use it. 184 * @param cnx the list of preferred connections. 185 */ 186 public void setPreferredConnections(LinkedHashSet<PreferredConnection> cnx) 187 { 188 preferredConnections.clear(); 189 preferredConnections.addAll(cnx); 190 } 191 192 /** 193 * Returns the list of LDAP URLs and connection type that are preferred to be 194 * used to connect to the servers. If a URL is on this list, when we have a 195 * server to which we can connect using that URL and the associated connection 196 * type we will try to use it. 197 * @return the list of preferred connections. 198 */ 199 public LinkedHashSet<PreferredConnection> getPreferredConnections() 200 { 201 return new LinkedHashSet<PreferredConnection>(preferredConnections); 202 } 203 204 /** 205 * Returns a Set containing all the servers that are registered in the ADS. 206 * @return a Set containing all the servers that are registered in the ADS. 207 */ 208 public Set<ServerDescriptor> getServers() 209 { 210 HashSet<ServerDescriptor> copy = new HashSet<ServerDescriptor>(); 211 copy.addAll(servers); 212 return copy; 213 } 214 215 /** 216 * Returns a Set containing the suffixes (replication topologies) that could 217 * be retrieved after the last call to reloadTopology. 218 * @return a Set containing the suffixes (replication topologies) that could 219 * be retrieved after the last call to reloadTopology. 220 */ 221 public Set<SuffixDescriptor> getSuffixes() 222 { 223 HashSet<SuffixDescriptor> copy = new HashSet<SuffixDescriptor>(); 224 copy.addAll(suffixes); 225 return copy; 226 } 227 228 /** 229 * Returns the filter to be used when retrieving information. 230 * @return the filter to be used when retrieving information. 231 */ 232 public TopologyCacheFilter getFilter() 233 { 234 return filter; 235 } 236 237 /** 238 * Method used to wait at most a certain time (MULTITHREAD_TIMEOUT) for the 239 * different threads to finish. 240 * @param threadSet the list of threads (we assume that they are started) 241 * that we must wait for. 242 */ 243 private void joinThreadSet(Set<ServerLoader> threadSet) 244 { 245 Date startDate = new Date(); 246 for (ServerLoader t : threadSet) 247 { 248 long timeToJoin = MULTITHREAD_TIMEOUT - System.currentTimeMillis() + 249 startDate.getTime(); 250 try 251 { 252 if (timeToJoin > 0) 253 { 254 t.join(MULTITHREAD_TIMEOUT); 255 } 256 } 257 catch (InterruptedException ie) 258 { 259 LOG.log(Level.INFO, ie + " caught and ignored", ie); 260 } 261 if (t.isAlive()) 262 { 263 t.interrupt(); 264 } 265 } 266 Date endDate = new Date(); 267 long workingTime = endDate.getTime() - startDate.getTime(); 268 LOG.log(Level.INFO, "Loading ended at "+ workingTime + " ms"); 269 } 270 271 /** 272 * Creates a ServerLoader object based on the provided server properties. 273 * @param serverProperties the server properties to be used to generate 274 * the ServerLoader. 275 * @return a ServerLoader object based on the provided server properties. 276 */ 277 private ServerLoader getServerLoader( 278 Map<ServerProperty,Object> serverProperties) 279 { 280 return new ServerLoader(serverProperties, dn, pwd, 281 trustManager == null ? null : trustManager.createCopy(), 282 getPreferredConnections(), getFilter()); 283 } 284 285 /** 286 * Returns the adsContext used by this TopologyCache. 287 * @return the adsContext used by this TopologyCache. 288 */ 289 public ADSContext getAdsContext() 290 { 291 return adsContext; 292 } 293 }