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    }