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 }