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 2007-2008 Sun Microsystems, Inc.
026 */
027
028 package org.opends.admin.ads;
029
030 import java.util.ArrayList;
031 import java.util.HashMap;
032 import java.util.HashSet;
033 import java.util.LinkedHashSet;
034 import java.util.Map;
035 import java.util.Set;
036
037 import javax.naming.NameNotFoundException;
038 import javax.naming.NamingEnumeration;
039 import javax.naming.NamingException;
040 import javax.naming.NameAlreadyBoundException;
041 import javax.naming.directory.*;
042 import javax.naming.ldap.InitialLdapContext;
043 import javax.naming.ldap.LdapName;
044 import javax.naming.ldap.Rdn;
045
046 import org.opends.admin.ads.util.ConnectionUtils;
047 import org.opends.quicksetup.util.Utils;
048
049 /**
050 * The object of this class represent an OpenDS server.
051 */
052 public class ServerDescriptor
053 {
054 private Map<ADSContext.ServerProperty, Object> adsProperties =
055 new HashMap<ADSContext.ServerProperty, Object>();
056 private Set<ReplicaDescriptor> replicas = new HashSet<ReplicaDescriptor>();
057 private Map<ServerProperty, Object> serverProperties =
058 new HashMap<ServerProperty, Object>();
059 private TopologyCacheException lastException;
060 /**
061 * Enumeration containing the different server properties that we can keep in
062 * the ServerProperty object.
063 */
064 public enum ServerProperty
065 {
066 /**
067 * The associated value is a String.
068 */
069 HOST_NAME,
070 /**
071 * The associated value is an ArrayList of Integer.
072 */
073 LDAP_PORT,
074 /**
075 * The associated value is an ArrayList of Integer.
076 */
077 LDAPS_PORT,
078 /**
079 * The associated value is an ArrayList of Boolean.
080 */
081 LDAP_ENABLED,
082 /**
083 * The associated value is an ArrayList of Boolean.
084 */
085 LDAPS_ENABLED,
086 /**
087 * The associated value is an ArrayList of Boolean.
088 */
089 STARTTLS_ENABLED,
090 /**
091 * The associated value is an ArrayList of Integer.
092 */
093 JMX_PORT,
094 /**
095 * The associated value is an ArrayList of Integer.
096 */
097 JMXS_PORT,
098 /**
099 * The associated value is an ArrayList of Boolean.
100 */
101 JMX_ENABLED,
102 /**
103 * The associated value is an ArrayList of Boolean.
104 */
105 JMXS_ENABLED,
106 /**
107 * The associated value is an Integer.
108 */
109 REPLICATION_SERVER_PORT,
110 /**
111 * The associated value is a Boolean.
112 */
113 IS_REPLICATION_SERVER,
114 /**
115 * The associated value is a Boolean.
116 */
117 IS_REPLICATION_ENABLED,
118 /**
119 * The associated value is a Boolean.
120 */
121 IS_REPLICATION_SECURE,
122 /**
123 * List of servers specified in the Replication Server configuration.
124 * This is a Set of String.
125 */
126 EXTERNAL_REPLICATION_SERVERS,
127 /**
128 * The associated value is an Integer.
129 */
130 REPLICATION_SERVER_ID,
131 /**
132 * The instance key-pair public-key certificate. The associated value is a
133 * byte[] (ds-cfg-public-key-certificate;binary).
134 */
135 INSTANCE_PUBLIC_KEY_CERTIFICATE,
136 /**
137 * The schema generation ID.
138 */
139 SCHEMA_GENERATION_ID
140 }
141
142 private ServerDescriptor()
143 {
144 }
145
146 /**
147 * Returns the replicas contained on the server.
148 * @return the replicas contained on the server.
149 */
150 public Set<ReplicaDescriptor> getReplicas()
151 {
152 Set<ReplicaDescriptor> copy = new HashSet<ReplicaDescriptor>();
153 copy.addAll(replicas);
154 return copy;
155 }
156
157 /**
158 * Sets the replicas contained on the server.
159 * @param replicas the replicas contained on the server.
160 */
161 public void setReplicas(Set<ReplicaDescriptor> replicas)
162 {
163 this.replicas.clear();
164 this.replicas.addAll(replicas);
165 }
166
167 /**
168 * Returns a Map containing the ADS properties of the server.
169 * @return a Map containing the ADS properties of the server.
170 */
171 public Map<ADSContext.ServerProperty, Object> getAdsProperties()
172 {
173 return adsProperties;
174 }
175
176 /**
177 * Returns a Map containing the properties of the server.
178 * @return a Map containing the properties of the server.
179 */
180 public Map<ServerProperty, Object> getServerProperties()
181 {
182 return serverProperties;
183 }
184
185 /**
186 * Tells whether this server is registered in the ADS or not.
187 * @return <CODE>true</CODE> if the server is registered in the ADS and
188 * <CODE>false</CODE> otherwise.
189 */
190 public boolean isRegistered()
191 {
192 return !adsProperties.isEmpty();
193 }
194
195 /**
196 * Tells whether this server is a replication server or not.
197 * @return <CODE>true</CODE> if the server is a replication server and
198 * <CODE>false</CODE> otherwise.
199 */
200 public boolean isReplicationServer()
201 {
202 return Boolean.TRUE.equals(
203 serverProperties.get(ServerProperty.IS_REPLICATION_SERVER));
204 }
205
206 /**
207 * Returns the String representation of this replication server based
208 * on the information we have ("hostname":"replication port") and
209 * <CODE>null</CODE> if this is not a replication server.
210 * @return the String representation of this replication server based
211 * on the information we have ("hostname":"replication port") and
212 * <CODE>null</CODE> if this is not a replication server.
213 */
214 public String getReplicationServerHostPort()
215 {
216 String hostPort = null;
217 if (isReplicationServer())
218 {
219 hostPort = getHostName().toLowerCase()+ ":" + getReplicationServerPort();
220 }
221 return hostPort;
222 }
223
224 /**
225 * Returns the replication server ID of this server and -1 if this is not a
226 * replications server.
227 * @return the replication server ID of this server and -1 if this is not a
228 * replications server.
229 */
230 public int getReplicationServerId()
231 {
232 int port = -1;
233 if (isReplicationServer())
234 {
235 port = (Integer)serverProperties.get(
236 ServerProperty.REPLICATION_SERVER_ID);
237 }
238 return port;
239 }
240
241 /**
242 * Returns the replication port of this server and -1 if this is not a
243 * replications server.
244 * @return the replication port of this server and -1 if this is not a
245 * replications server.
246 */
247 public int getReplicationServerPort()
248 {
249 int port = -1;
250 if (isReplicationServer())
251 {
252 port = (Integer)serverProperties.get(
253 ServerProperty.REPLICATION_SERVER_PORT);
254 }
255 return port;
256 }
257
258 /**
259 * Returns whether the communication with the replication port on the server
260 * is encrypted or not.
261 * @return <CODE>true</CODE> if the communication with the replication port on
262 * the server is encrypted and <CODE>false</CODE> otherwise.
263 */
264 public boolean isReplicationSecure()
265 {
266 boolean isReplicationSecure;
267 if (isReplicationServer())
268 {
269 isReplicationSecure = Boolean.TRUE.equals(serverProperties.get(
270 ServerProperty.IS_REPLICATION_SECURE));
271 }
272 else
273 {
274 isReplicationSecure = false;
275 }
276 return isReplicationSecure;
277 }
278
279 /**
280 * Sets the ADS properties of the server.
281 * @param adsProperties a Map containing the ADS properties of the server.
282 */
283 public void setAdsProperties(
284 Map<ADSContext.ServerProperty, Object> adsProperties)
285 {
286 this.adsProperties = adsProperties;
287 }
288
289 /**
290 * Returns the host name of the server.
291 * @return the host name of the server.
292 */
293 public String getHostName()
294 {
295 String host = (String)serverProperties.get(ServerProperty.HOST_NAME);
296 if (host == null)
297 {
298 host = (String)adsProperties.get(ADSContext.ServerProperty.HOST_NAME);
299 }
300 return host;
301 }
302
303 /**
304 * Returns the URL to access this server using LDAP. Returns
305 * <CODE>null</CODE> if the server is not configured to listen on an LDAP
306 * port.
307 * @return the URL to access this server using LDAP.
308 */
309 public String getLDAPURL()
310 {
311 String ldapUrl = null;
312 String host = getHostName();
313 int port = -1;
314
315 if (!serverProperties.isEmpty())
316 {
317 ArrayList s = (ArrayList)serverProperties.get(
318 ServerProperty.LDAP_ENABLED);
319 ArrayList p = (ArrayList)serverProperties.get(
320 ServerProperty.LDAP_PORT);
321 if (s != null)
322 {
323 for (int i=0; i<s.size(); i++)
324 {
325 if (Boolean.TRUE.equals(s.get(i)))
326 {
327 port = (Integer)p.get(i);
328 break;
329 }
330 }
331 }
332 }
333 if (port != -1)
334 {
335 ldapUrl = ConnectionUtils.getLDAPUrl(host, port, false);
336 }
337 return ldapUrl;
338 }
339
340 /**
341 * Returns the URL to access this server using LDAPS. Returns
342 * <CODE>null</CODE> if the server is not configured to listen on an LDAPS
343 * port.
344 * @return the URL to access this server using LDAP.
345 */
346 public String getLDAPsURL()
347 {
348 String ldapsUrl = null;
349 String host = getHostName();
350 int port = -1;
351
352 if (!serverProperties.isEmpty())
353 {
354 ArrayList s = (ArrayList)serverProperties.get(
355 ServerProperty.LDAPS_ENABLED);
356 ArrayList p = (ArrayList)serverProperties.get(
357 ServerProperty.LDAPS_PORT);
358 if (s != null)
359 {
360 for (int i=0; i<s.size(); i++)
361 {
362 if (Boolean.TRUE.equals(s.get(i)))
363 {
364 port = (Integer)p.get(i);
365 break;
366 }
367 }
368 }
369 }
370 if (port != -1)
371 {
372 ldapsUrl = ConnectionUtils.getLDAPUrl(host, port, true);
373 }
374 return ldapsUrl;
375 }
376
377 /**
378 * Returns a String of type host-name:port-number for the server. If
379 * the provided securePreferred is set to true the port that will be used
380 * (if LDAPS is enabled) will be the LDAPS port.
381 * @param securePreferred whether to try to use the secure port as part
382 * of the returning String or not.
383 * @return a String of type host-name:port-number for the server.
384 */
385 public String getHostPort(boolean securePreferred)
386 {
387 String host = getHostName();
388 int port = -1;
389
390 if (!serverProperties.isEmpty())
391 {
392 ArrayList s = (ArrayList)serverProperties.get(
393 ServerProperty.LDAP_ENABLED);
394 ArrayList p = (ArrayList)serverProperties.get(
395 ServerProperty.LDAP_PORT);
396 if (s != null)
397 {
398 for (int i=0; i<s.size(); i++)
399 {
400 if (Boolean.TRUE.equals(s.get(i)))
401 {
402 port = (Integer)p.get(i);
403 break;
404 }
405 }
406 }
407 if (securePreferred)
408 {
409 s = (ArrayList)serverProperties.get(
410 ServerProperty.LDAPS_ENABLED);
411 p = (ArrayList)serverProperties.get(ServerProperty.LDAPS_PORT);
412 if (s != null)
413 {
414 for (int i=0; i<s.size(); i++)
415 {
416 if (Boolean.TRUE.equals(s.get(i)))
417 {
418 port = (Integer)p.get(i);
419 break;
420 }
421 }
422 }
423 }
424 }
425 else
426 {
427 boolean secure;
428
429 Object v = adsProperties.get(ADSContext.ServerProperty.LDAPS_ENABLED);
430 secure = securePreferred && "true".equalsIgnoreCase(String.valueOf(v));
431 try
432 {
433 if (secure)
434 {
435 port = Integer.parseInt((String)adsProperties.get(
436 ADSContext.ServerProperty.LDAPS_PORT));
437 }
438 else
439 {
440 port = Integer.parseInt((String)adsProperties.get(
441 ADSContext.ServerProperty.LDAP_PORT));
442 }
443 }
444 catch (Throwable t)
445 {
446 /* ignore */
447 }
448 }
449 return host + ":" + port;
450 }
451
452 /**
453 * Returns an Id that is unique for this server.
454 * @return an Id that is unique for this server.
455 */
456 public String getId()
457 {
458 StringBuilder buf = new StringBuilder();
459 if (serverProperties.size() > 0)
460 {
461 buf.append(serverProperties.get(ServerProperty.HOST_NAME));
462 ServerProperty [] props =
463 {
464 ServerProperty.LDAP_PORT, ServerProperty.LDAPS_PORT,
465 ServerProperty.LDAP_ENABLED, ServerProperty.LDAPS_ENABLED
466 };
467 for (ServerProperty prop : props) {
468 ArrayList s = (ArrayList) serverProperties.get(prop);
469 for (Object o : s) {
470 buf.append(":").append(o);
471 }
472 }
473 }
474 else
475 {
476 ADSContext.ServerProperty[] props =
477 {
478 ADSContext.ServerProperty.HOST_NAME,
479 ADSContext.ServerProperty.LDAP_PORT,
480 ADSContext.ServerProperty.LDAPS_PORT,
481 ADSContext.ServerProperty.LDAP_ENABLED,
482 ADSContext.ServerProperty.LDAPS_ENABLED
483 };
484 for (int i=0; i<props.length; i++)
485 {
486 if (i != 0)
487 {
488 buf.append(":");
489 }
490 buf.append(adsProperties.get(props[i]));
491 }
492 }
493 return buf.toString();
494 }
495
496 /**
497 * Returns the instance-key public-key certificate retrieved from the
498 * truststore backend of the instance referenced through this descriptor.
499 *
500 * @return The public-key certificate of the instance.
501 */
502 public byte[] getInstancePublicKeyCertificate()
503 {
504 return((byte[])
505 serverProperties.get(ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE));
506 }
507
508 /**
509 * Returns the schema generation ID of the server.
510 * @return the schema generation ID of the server.
511 */
512 public String getSchemaReplicationID()
513 {
514 return (String)serverProperties.get(ServerProperty.SCHEMA_GENERATION_ID);
515 }
516
517 /**
518 * Returns the last exception that was encountered reading the configuration
519 * of the server. Returns null if there was no problem loading the
520 * configuration of the server.
521 * @return the last exception that was encountered reading the configuration
522 * of the server. Returns null if there was no problem loading the
523 * configuration of the server.
524 */
525 public TopologyCacheException getLastException()
526 {
527 return lastException;
528 }
529
530 /**
531 * Sets the last exception that occurred while reading the configuration of
532 * the server.
533 * @param lastException the last exception that occurred while reading the
534 * configuration of the server.
535 */
536 public void setLastException(TopologyCacheException lastException)
537 {
538 this.lastException = lastException;
539 }
540
541 /**
542 * This methods updates the ADS properties (the ones that were read from
543 * the ADS) with the contents of the server properties (the ones that were
544 * read directly from the server).
545 */
546 public void updateAdsPropertiesWithServerProperties()
547 {
548 adsProperties.put(ADSContext.ServerProperty.HOST_NAME, getHostName());
549 ServerProperty[][] sProps =
550 {
551 {ServerProperty.LDAP_ENABLED, ServerProperty.LDAP_PORT},
552 {ServerProperty.LDAPS_ENABLED, ServerProperty.LDAPS_PORT},
553 {ServerProperty.JMX_ENABLED, ServerProperty.JMX_PORT},
554 {ServerProperty.JMXS_ENABLED, ServerProperty.JMXS_PORT}
555 };
556 ADSContext.ServerProperty[][] adsProps =
557 {
558 {ADSContext.ServerProperty.LDAP_ENABLED,
559 ADSContext.ServerProperty.LDAP_PORT},
560 {ADSContext.ServerProperty.LDAPS_ENABLED,
561 ADSContext.ServerProperty.LDAPS_PORT},
562 {ADSContext.ServerProperty.JMX_ENABLED,
563 ADSContext.ServerProperty.JMX_PORT},
564 {ADSContext.ServerProperty.JMXS_ENABLED,
565 ADSContext.ServerProperty.JMXS_PORT}
566 };
567
568 for (int i=0; i<sProps.length; i++)
569 {
570 ArrayList s = (ArrayList)serverProperties.get(sProps[i][0]);
571 ArrayList p = (ArrayList)serverProperties.get(sProps[i][1]);
572 if (s != null)
573 {
574 int port = -1;
575 for (int j=0; j<s.size(); j++)
576 {
577 if (Boolean.TRUE.equals(s.get(j)))
578 {
579 port = (Integer)p.get(j);
580 break;
581 }
582 }
583 if (port == -1)
584 {
585 adsProperties.put(adsProps[i][0], "false");
586 if (p.size() > 0)
587 {
588 port = (Integer)p.iterator().next();
589 }
590 }
591 else
592 {
593 adsProperties.put(adsProps[i][0], "true");
594 }
595 adsProperties.put(adsProps[i][1], String.valueOf(port));
596 }
597 }
598
599 ArrayList array = (ArrayList)serverProperties.get(
600 ServerProperty.STARTTLS_ENABLED);
601 boolean startTLSEnabled = false;
602 if ((array != null) && !array.isEmpty())
603 {
604 startTLSEnabled = Boolean.TRUE.equals(array.get(array.size() -1));
605 }
606 adsProperties.put(ADSContext.ServerProperty.STARTTLS_ENABLED,
607 startTLSEnabled ? "true" : "false");
608 adsProperties.put(ADSContext.ServerProperty.ID, getHostPort(true));
609 adsProperties.put(ADSContext.ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE,
610 getInstancePublicKeyCertificate());
611 }
612
613 /**
614 * Creates a ServerDescriptor object based on some ADS properties provided.
615 * @param adsProperties the ADS properties of the server.
616 * @return a ServerDescriptor object that corresponds to the provided ADS
617 * properties.
618 */
619 public static ServerDescriptor createStandalone(
620 Map<ADSContext.ServerProperty, Object> adsProperties)
621 {
622 ServerDescriptor desc = new ServerDescriptor();
623 desc.setAdsProperties(adsProperties);
624 return desc;
625 }
626
627 /**
628 * Creates a ServerDescriptor object based on the configuration that we read
629 * using the provided InitialLdapContext.
630 * @param ctx the InitialLdapContext that will be used to read the
631 * configuration of the server.
632 * @param filter the topology cache filter describing the information that
633 * must be retrieved.
634 * @return a ServerDescriptor object that corresponds to the read
635 * configuration.
636 * @throws NamingException if a problem occurred reading the server
637 * configuration.
638 */
639 public static ServerDescriptor createStandalone(InitialLdapContext ctx,
640 TopologyCacheFilter filter)
641 throws NamingException
642 {
643 ServerDescriptor desc = new ServerDescriptor();
644
645
646 updateLdapConfiguration(desc, ctx, filter);
647 updateJmxConfiguration(desc, ctx, filter);
648 updateReplicas(desc, ctx, filter);
649 updateReplication(desc, ctx, filter);
650 updatePublicKeyCertificate(desc, ctx, filter);
651 updateMiscellaneous(desc, ctx, filter);
652
653 desc.serverProperties.put(ServerProperty.HOST_NAME,
654 ConnectionUtils.getHostName(ctx));
655
656 return desc;
657 }
658
659 private static void updateLdapConfiguration(ServerDescriptor desc,
660 InitialLdapContext ctx, TopologyCacheFilter cacheFilter)
661 throws NamingException
662 {
663 SearchControls ctls = new SearchControls();
664 ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
665 ctls.setReturningAttributes(
666 new String[] {
667 "ds-cfg-enabled",
668 "ds-cfg-listen-address",
669 "ds-cfg-listen-port",
670 "ds-cfg-use-ssl",
671 "ds-cfg-allow-start-tls",
672 "objectclass"
673 });
674 String filter = "(objectclass=ds-cfg-ldap-connection-handler)";
675
676 LdapName jndiName = new LdapName("cn=config");
677 NamingEnumeration listeners = ctx.search(jndiName, filter, ctls);
678
679 ArrayList<Integer> ldapPorts = new ArrayList<Integer>();
680 ArrayList<Integer> ldapsPorts = new ArrayList<Integer>();
681 ArrayList<Boolean> ldapEnabled = new ArrayList<Boolean>();
682 ArrayList<Boolean> ldapsEnabled = new ArrayList<Boolean>();
683 ArrayList<Boolean> startTLSEnabled = new ArrayList<Boolean>();
684
685 desc.serverProperties.put(ServerProperty.LDAP_PORT, ldapPorts);
686 desc.serverProperties.put(ServerProperty.LDAPS_PORT, ldapsPorts);
687 desc.serverProperties.put(ServerProperty.LDAP_ENABLED, ldapEnabled);
688 desc.serverProperties.put(ServerProperty.LDAPS_ENABLED, ldapsEnabled);
689 desc.serverProperties.put(ServerProperty.STARTTLS_ENABLED, startTLSEnabled);
690
691 while(listeners.hasMore())
692 {
693 SearchResult sr = (SearchResult)listeners.next();
694
695 String port = getFirstValue(sr, "ds-cfg-listen-port");
696
697 boolean isSecure = "true".equalsIgnoreCase(
698 getFirstValue(sr, "ds-cfg-use-ssl"));
699
700 boolean enabled = "true".equalsIgnoreCase(
701 getFirstValue(sr, "ds-cfg-enabled"));
702 if (isSecure)
703 {
704 ldapsPorts.add(new Integer(port));
705 ldapsEnabled.add(enabled);
706 }
707 else
708 {
709 ldapPorts.add(new Integer(port));
710 ldapEnabled.add(enabled);
711 enabled = "true".equalsIgnoreCase(
712 getFirstValue(sr, "ds-cfg-allow-start-tls"));
713 startTLSEnabled.add(enabled);
714 }
715 }
716 }
717
718 private static void updateJmxConfiguration(ServerDescriptor desc,
719 InitialLdapContext ctx, TopologyCacheFilter cacheFilter)
720 throws NamingException
721 {
722 SearchControls ctls = new SearchControls();
723 ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
724 ctls.setReturningAttributes(
725 new String[] {
726 "ds-cfg-enabled",
727 "ds-cfg-listen-address",
728 "ds-cfg-listen-port",
729 "ds-cfg-use-ssl",
730 "objectclass"
731 });
732 String filter = "(objectclass=ds-cfg-jmx-connection-handler)";
733
734 LdapName jndiName = new LdapName("cn=config");
735 NamingEnumeration listeners = ctx.search(jndiName, filter, ctls);
736
737 ArrayList<Integer> jmxPorts = new ArrayList<Integer>();
738 ArrayList<Integer> jmxsPorts = new ArrayList<Integer>();
739 ArrayList<Boolean> jmxEnabled = new ArrayList<Boolean>();
740 ArrayList<Boolean> jmxsEnabled = new ArrayList<Boolean>();
741
742 desc.serverProperties.put(ServerProperty.JMX_PORT, jmxPorts);
743 desc.serverProperties.put(ServerProperty.JMXS_PORT, jmxsPorts);
744 desc.serverProperties.put(ServerProperty.JMX_ENABLED, jmxEnabled);
745 desc.serverProperties.put(ServerProperty.JMXS_ENABLED, jmxsEnabled);
746
747 while(listeners.hasMore())
748 {
749 SearchResult sr = (SearchResult)listeners.next();
750
751 String port = getFirstValue(sr, "ds-cfg-listen-port");
752
753 boolean isSecure = "true".equalsIgnoreCase(
754 getFirstValue(sr, "ds-cfg-use-ssl"));
755
756 boolean enabled = "true".equalsIgnoreCase(
757 getFirstValue(sr, "ds-cfg-enabled"));
758 if (isSecure)
759 {
760 jmxsPorts.add(new Integer(port));
761 jmxsEnabled.add(enabled);
762 }
763 else
764 {
765 jmxPorts.add(new Integer(port));
766 jmxEnabled.add(enabled);
767 }
768 }
769 }
770
771 private static void updateReplicas(ServerDescriptor desc,
772 InitialLdapContext ctx, TopologyCacheFilter cacheFilter)
773 throws NamingException
774 {
775 if (!cacheFilter.searchBaseDNInformation())
776 {
777 return;
778 }
779 SearchControls ctls = new SearchControls();
780 ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
781 ctls.setReturningAttributes(
782 new String[] {
783 "ds-cfg-base-dn",
784 "ds-cfg-backend-id"
785 });
786 String filter = "(objectclass=ds-cfg-backend)";
787
788 LdapName jndiName = new LdapName("cn=config");
789 NamingEnumeration databases = ctx.search(jndiName, filter, ctls);
790
791 while(databases.hasMore())
792 {
793 SearchResult sr = (SearchResult)databases.next();
794
795 String id = getFirstValue(sr, "ds-cfg-backend-id");
796
797 if (!isConfigBackend(id) || isSchemaBackend(id))
798 {
799 Set<String> baseDns = getValues(sr, "ds-cfg-base-dn");
800
801 Set<String> entries;
802 if (cacheFilter.searchMonitoringInformation())
803 {
804 entries = getBaseDNEntryCount(ctx, id);
805 }
806 else
807 {
808 entries = new HashSet<String>();
809 }
810
811 Set<ReplicaDescriptor> replicas = desc.getReplicas();
812 for (String baseDn : baseDns)
813 {
814 boolean addReplica = cacheFilter.searchAllBaseDNs();
815 if (!addReplica)
816 {
817 for (String dn : cacheFilter.getBaseDNsToSearch())
818 {
819 addReplica = Utils.areDnsEqual(dn, baseDn);
820 if (addReplica)
821 {
822 break;
823 }
824 }
825 }
826 if(addReplica)
827 {
828 SuffixDescriptor suffix = new SuffixDescriptor();
829 suffix.setDN(baseDn);
830 ReplicaDescriptor replica = new ReplicaDescriptor();
831 replica.setServer(desc);
832 replicas.add(replica);
833 HashSet<ReplicaDescriptor> r = new HashSet<ReplicaDescriptor>();
834 r.add(replica);
835 suffix.setReplicas(r);
836 replica.setSuffix(suffix);
837 int nEntries = -1;
838 for (String s : entries)
839 {
840 int index = s.indexOf(" ");
841 if (index != -1)
842 {
843 String dn = s.substring(index + 1);
844 if (Utils.areDnsEqual(baseDn, dn))
845 {
846 try
847 {
848 nEntries = Integer.parseInt(s.substring(0, index));
849 }
850 catch (Throwable t)
851 {
852 /* Ignore */
853 }
854 break;
855 }
856 }
857 }
858 replica.setEntries(nEntries);
859 }
860 }
861 desc.setReplicas(replicas);
862 }
863 }
864 }
865
866 private static void updateReplication(ServerDescriptor desc,
867 InitialLdapContext ctx, TopologyCacheFilter cacheFilter)
868 throws NamingException
869 {
870 boolean replicationEnabled = false;
871 boolean oneDomainReplicated = false;
872 SearchControls ctls = new SearchControls();
873 ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
874 ctls.setReturningAttributes(
875 new String[] {
876 "ds-cfg-enabled"
877 });
878 String filter = "(objectclass=ds-cfg-synchronization-provider)";
879
880 LdapName jndiName = new LdapName(
881 "cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config");
882
883 try
884 {
885 NamingEnumeration syncProviders = ctx.search(jndiName, filter, ctls);
886
887 while(syncProviders.hasMore())
888 {
889 SearchResult sr = (SearchResult)syncProviders.next();
890
891 if ("true".equalsIgnoreCase(getFirstValue(sr,
892 "ds-cfg-enabled")))
893 {
894 replicationEnabled = true;
895 }
896 }
897 }
898 catch (NameNotFoundException nse)
899 {
900 /* ignore */
901 }
902 desc.serverProperties.put(ServerProperty.IS_REPLICATION_ENABLED,
903 replicationEnabled ? Boolean.TRUE : Boolean.FALSE);
904
905 if (cacheFilter.searchBaseDNInformation())
906 {
907 ctls = new SearchControls();
908 ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
909 ctls.setReturningAttributes(
910 new String[] {
911 "ds-cfg-base-dn",
912 "ds-cfg-replication-server",
913 "ds-cfg-server-id"
914 });
915 filter = "(objectclass=ds-cfg-replication-domain)";
916
917 jndiName = new LdapName(
918 "cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config");
919
920 try
921 {
922 NamingEnumeration syncProviders = ctx.search(jndiName, filter, ctls);
923
924 while(syncProviders.hasMore())
925 {
926 SearchResult sr = (SearchResult)syncProviders.next();
927
928 int id = Integer.parseInt(
929 getFirstValue(sr, "ds-cfg-server-id"));
930 Set<String> replicationServers = getValues(sr,
931 "ds-cfg-replication-server");
932 Set<String> dns = getValues(sr, "ds-cfg-base-dn");
933 oneDomainReplicated = dns.size() > 0;
934 for (String dn : dns)
935 {
936 for (ReplicaDescriptor replica : desc.getReplicas())
937 {
938 if (areDnsEqual(replica.getSuffix().getDN(), dn))
939 {
940 replica.setReplicationId(id);
941 // Keep the values of the replication servers in lower case
942 // to make use of Sets as String simpler.
943 LinkedHashSet<String> repServers = new LinkedHashSet<String>();
944 for (String s: replicationServers)
945 {
946 repServers.add(s.toLowerCase());
947 }
948 replica.setReplicationServers(repServers);
949 }
950 }
951 }
952 }
953 }
954 catch (NameNotFoundException nse)
955 {
956 /* ignore */
957 }
958 }
959
960 ctls = new SearchControls();
961 ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
962 ctls.setReturningAttributes(
963 new String[] {
964 "ds-cfg-replication-port", "ds-cfg-replication-server",
965 "ds-cfg-replication-server-id"
966 });
967 filter = "(objectclass=ds-cfg-replication-server)";
968
969 jndiName = new LdapName("cn=Replication Server,cn=Multimaster "+
970 "Synchronization,cn=Synchronization Providers,cn=config");
971
972 desc.serverProperties.put(ServerProperty.IS_REPLICATION_SERVER,
973 Boolean.FALSE);
974 try
975 {
976 NamingEnumeration entries = ctx.search(jndiName, filter, ctls);
977
978 while(entries.hasMore())
979 {
980 SearchResult sr = (SearchResult)entries.next();
981
982 desc.serverProperties.put(ServerProperty.IS_REPLICATION_SERVER,
983 Boolean.TRUE);
984 String v = getFirstValue(sr, "ds-cfg-replication-port");
985 desc.serverProperties.put(ServerProperty.REPLICATION_SERVER_PORT,
986 Integer.parseInt(v));
987 v = getFirstValue(sr, "ds-cfg-replication-server-id");
988 desc.serverProperties.put(ServerProperty.REPLICATION_SERVER_ID,
989 Integer.parseInt(v));
990 Set<String> values = getValues(sr, "ds-cfg-replication-server");
991 // Keep the values of the replication servers in lower case
992 // to make use of Sets as String simpler.
993 LinkedHashSet<String> repServers = new LinkedHashSet<String>();
994 for (String s: values)
995 {
996 repServers.add(s.toLowerCase());
997 }
998 desc.serverProperties.put(ServerProperty.EXTERNAL_REPLICATION_SERVERS,
999 repServers);
1000 }
1001 }
1002 catch (NameNotFoundException nse)
1003 {
1004 /* ignore */
1005 }
1006
1007 if (cacheFilter.searchMonitoringInformation())
1008 {
1009 ctls = new SearchControls();
1010 ctls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
1011 ctls.setReturningAttributes(
1012 new String[] {
1013 "approx-older-change-not-synchronized-millis", "missing-changes",
1014 "base-dn", "server-id"
1015 });
1016 filter = "(missing-changes=*)";
1017
1018 jndiName = new LdapName("cn=monitor");
1019
1020 if (oneDomainReplicated)
1021 {
1022 try
1023 {
1024 NamingEnumeration monitorEntries = ctx.search(jndiName, filter, ctls);
1025
1026 while(monitorEntries.hasMore())
1027 {
1028 SearchResult sr = (SearchResult)monitorEntries.next();
1029
1030 String dn = getFirstValue(sr, "base-dn");
1031 int replicaId = -1;
1032 try
1033 {
1034 replicaId = new Integer(getFirstValue(sr, "server-id"));
1035 }
1036 catch (Throwable t)
1037 {
1038 }
1039
1040 for (ReplicaDescriptor replica: desc.getReplicas())
1041 {
1042 if (Utils.areDnsEqual(dn, replica.getSuffix().getDN()) &&
1043 replica.isReplicated() &&
1044 (replica.getReplicationId() == replicaId))
1045 {
1046 try
1047 {
1048 replica.setAgeOfOldestMissingChange(
1049 new Long(getFirstValue(sr,
1050 "approx-older-change-not-synchronized-millis")));
1051 }
1052 catch (Throwable t)
1053 {
1054 }
1055 try
1056 {
1057 replica.setMissingChanges(
1058 new Integer(getFirstValue(sr, "missing-changes")));
1059 }
1060 catch (Throwable t)
1061 {
1062 }
1063 }
1064 }
1065 }
1066 }
1067 catch (NameNotFoundException nse)
1068 {
1069 }
1070 }
1071 }
1072
1073 boolean replicationSecure = false;
1074 if (replicationEnabled)
1075 {
1076 ctls = new SearchControls();
1077 ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
1078 ctls.setReturningAttributes(
1079 new String[] {"ds-cfg-ssl-encryption"});
1080 filter = "(objectclass=ds-cfg-crypto-manager)";
1081
1082 jndiName = new LdapName("cn=Crypto Manager,cn=config");
1083
1084 NamingEnumeration entries = ctx.search(jndiName, filter, ctls);
1085
1086 while(entries.hasMore())
1087 {
1088 SearchResult sr = (SearchResult)entries.next();
1089
1090 String v = getFirstValue(sr, "ds-cfg-ssl-encryption");
1091 replicationSecure = "true".equalsIgnoreCase(v);
1092 }
1093 }
1094 desc.serverProperties.put(ServerProperty.IS_REPLICATION_SECURE,
1095 replicationSecure ? Boolean.TRUE : Boolean.FALSE);
1096 }
1097
1098 /**
1099 Updates the instance key public-key certificate value of this context from
1100 the local truststore of the instance bound by this context. Any current
1101 value of the certificate is overwritten. The intent of this method is to
1102 retrieve the instance-key public-key certificate when this context is bound
1103 to an instance, and cache it for later use in registering the instance into
1104 ADS.
1105 @param desc The map to update with the instance key-pair public-key
1106 certificate.
1107 @param ctx The bound server instance.
1108 @throws NamingException if unable to retrieve certificate from bound
1109 instance.
1110 */
1111 private static void updatePublicKeyCertificate(ServerDescriptor desc,
1112 InitialLdapContext ctx, TopologyCacheFilter filter) throws NamingException
1113 {
1114 /* TODO: this DN is declared in some core constants file. Create a constants
1115 file for the installer and import it into the core. */
1116 final String dnStr = "ds-cfg-key-id=ads-certificate,cn=ads-truststore";
1117 final LdapName dn = new LdapName(dnStr);
1118 for (int i = 0; i < 2 ; ++i) {
1119 /* If the entry does not exist in the instance's truststore backend, add
1120 it (which induces the CryptoManager to create the public-key
1121 certificate attribute), then repeat the search. */
1122 try {
1123 final SearchControls searchControls = new SearchControls();
1124 searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
1125 final String attrIDs[] = { "ds-cfg-public-key-certificate;binary" };
1126 searchControls.setReturningAttributes(attrIDs);
1127 final SearchResult certEntry = ctx.search(dn,
1128 "(objectclass=ds-cfg-instance-key)", searchControls).next();
1129 final Attribute certAttr = certEntry.getAttributes().get(attrIDs[0]);
1130 if (null != certAttr) {
1131 /* attribute ds-cfg-public-key-certificate is a MUST in the schema */
1132 desc.serverProperties.put(
1133 ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE,
1134 certAttr.get());
1135 }
1136 break;
1137 }
1138 catch (NameNotFoundException x) {
1139 if (0 == i) {
1140 /* Poke CryptoManager to initialize truststore. Note the special
1141 attribute in the request. */
1142 final Attributes attrs = new BasicAttributes();
1143 final Attribute oc = new BasicAttribute("objectclass");
1144 oc.add("top");
1145 oc.add("ds-cfg-self-signed-cert-request");
1146 attrs.put(oc);
1147 ctx.createSubcontext(dn, attrs).close();
1148 }
1149 else {
1150 throw x;
1151 }
1152 }
1153 }
1154 }
1155
1156 private static void updateMiscellaneous(ServerDescriptor desc,
1157 InitialLdapContext ctx, TopologyCacheFilter cacheFilter)
1158 throws NamingException
1159 {
1160 SearchControls ctls = new SearchControls();
1161 ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
1162 ctls.setReturningAttributes(
1163 new String[] {
1164 "ds-sync-generation-id"
1165 });
1166 String filter = "|(objectclass=*)(objectclass=ldapsubentry)";
1167
1168 LdapName jndiName = new LdapName("cn=schema");
1169 NamingEnumeration listeners = ctx.search(jndiName, filter, ctls);
1170
1171 while(listeners.hasMore())
1172 {
1173 SearchResult sr = (SearchResult)listeners.next();
1174
1175 desc.serverProperties.put(ServerProperty.SCHEMA_GENERATION_ID,
1176 getFirstValue(sr, "ds-sync-generation-id"));
1177 }
1178 }
1179
1180 /**
1181 Seeds the bound instance's local ads-truststore with a set of instance
1182 key-pair public key certificates. The result is the instance will trust any
1183 instance posessing the private key corresponding to one of the public-key
1184 certificates. This trust is necessary at least to initialize replication,
1185 which uses the trusted certificate entries in the ads-truststore for server
1186 authentication.
1187 @param ctx The bound instance.
1188 @param keyEntryMap The set of valid (i.e., not tagged as compromised)
1189 instance key-pair public-key certificate entries in ADS represented as a map
1190 from keyID to public-key certificate (binary).
1191 @throws NamingException in case an error occurs while updating the instance's
1192 ads-truststore via LDAP.
1193 */
1194 public static void seedAdsTrustStore(
1195 InitialLdapContext ctx,
1196 Map<String, byte[]> keyEntryMap)
1197 throws NamingException
1198 {
1199 /* TODO: this DN is declared in some core constants file. Create a
1200 constants file for the installer and import it into the core. */
1201 final String truststoreDnStr = "cn=ads-truststore";
1202 final Attribute oc = new BasicAttribute("objectclass");
1203 oc.add("top");
1204 oc.add("ds-cfg-instance-key");
1205 for (Map.Entry<String, byte[]> keyEntry : keyEntryMap.entrySet()){
1206 final BasicAttributes keyAttrs = new BasicAttributes();
1207 keyAttrs.put(oc);
1208 final Attribute rdnAttr = new BasicAttribute(
1209 ADSContext.ServerProperty.INSTANCE_KEY_ID.getAttributeName(),
1210 keyEntry.getKey());
1211 keyAttrs.put(rdnAttr);
1212 keyAttrs.put(new BasicAttribute(
1213 ADSContext.ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE.
1214 getAttributeName() + ";binary", keyEntry.getValue()));
1215 final LdapName keyDn = new LdapName((new StringBuilder(rdnAttr.getID()))
1216 .append("=").append(Rdn.escapeValue(rdnAttr.get())).append(",")
1217 .append(truststoreDnStr).toString());
1218 try {
1219 ctx.createSubcontext(keyDn, keyAttrs).close();
1220 }
1221 catch(NameAlreadyBoundException x){
1222 ctx.destroySubcontext(keyDn);
1223 ctx.createSubcontext(keyDn, keyAttrs).close();
1224 }
1225 }
1226 }
1227
1228 /**
1229 * Returns the values of the ds-base-dn-entry count attributes for the given
1230 * backend monitor entry using the provided InitialLdapContext.
1231 * @param ctx the InitialLdapContext to use to update the configuration.
1232 * @param backendID the id of the backend.
1233 * @return the values of the ds-base-dn-entry count attribute.
1234 * @throws NamingException if there was an error.
1235 */
1236 private static Set<String> getBaseDNEntryCount(InitialLdapContext ctx,
1237 String backendID) throws NamingException
1238 {
1239 LinkedHashSet<String> v = new LinkedHashSet<String>();
1240 SearchControls ctls = new SearchControls();
1241 ctls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
1242 ctls.setReturningAttributes(
1243 new String[] {
1244 "ds-base-dn-entry-count"
1245 });
1246 String filter = "(ds-backend-id="+backendID+")";
1247
1248 LdapName jndiName = new LdapName("cn=monitor");
1249 NamingEnumeration listeners = ctx.search(jndiName, filter, ctls);
1250
1251 while(listeners.hasMore())
1252 {
1253 SearchResult sr = (SearchResult)listeners.next();
1254
1255 v.addAll(getValues(sr, "ds-base-dn-entry-count"));
1256 }
1257 return v;
1258 }
1259
1260 /*
1261 * The following 2 methods are convenience methods to retrieve String values
1262 * from an entry.
1263 */
1264 private static String getFirstValue(SearchResult entry, String attrName)
1265 throws NamingException
1266 {
1267 return ConnectionUtils.getFirstValue(entry, attrName);
1268 }
1269
1270 private static Set<String> getValues(SearchResult entry, String attrName)
1271 throws NamingException
1272 {
1273 return ConnectionUtils.getValues(entry, attrName);
1274 }
1275
1276 /**
1277 * An convenience method to know if the provided ID corresponds to a
1278 * configuration backend or not.
1279 * @param id the backend ID to analyze
1280 * @return <CODE>true</CODE> if the the id corresponds to a configuration
1281 * backend and <CODE>false</CODE> otherwise.
1282 */
1283 private static boolean isConfigBackend(String id)
1284 {
1285 return "tasks".equalsIgnoreCase(id) ||
1286 "schema".equalsIgnoreCase(id) ||
1287 "config".equalsIgnoreCase(id) ||
1288 "monitor".equalsIgnoreCase(id) ||
1289 "backup".equalsIgnoreCase(id) ||
1290 "ads-truststore".equalsIgnoreCase(id);
1291 }
1292
1293 /**
1294 * An convenience method to know if the provided ID corresponds to the schema
1295 * backend or not.
1296 * @param id the backend ID to analyze
1297 * @return <CODE>true</CODE> if the the id corresponds to the schema backend
1298 * and <CODE>false</CODE> otherwise.
1299 */
1300 private static boolean isSchemaBackend(String id)
1301 {
1302 return "schema".equalsIgnoreCase(id);
1303 }
1304 /**
1305 * Returns <CODE>true</CODE> if the the provided strings represent the same
1306 * DN and <CODE>false</CODE> otherwise.
1307 * @param dn1 the first dn to compare.
1308 * @param dn2 the second dn to compare.
1309 * @return <CODE>true</CODE> if the the provided strings represent the same
1310 * DN and <CODE>false</CODE> otherwise.
1311 */
1312 private static boolean areDnsEqual(String dn1, String dn2)
1313 {
1314 boolean areDnsEqual = false;
1315 try
1316 {
1317 LdapName name1 = new LdapName(dn1);
1318 LdapName name2 = new LdapName(dn2);
1319 areDnsEqual = name1.equals(name2);
1320 } catch (Exception ex)
1321 {
1322 /* ignore */
1323 }
1324 return areDnsEqual;
1325 }
1326 }