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.server.core; 029 030 import org.opends.server.types.DN; 031 import org.opends.server.types.DirectoryException; 032 import org.opends.server.types.ResultCode; 033 import org.opends.server.api.Backend; 034 import static org.opends.server.util.Validator.ensureNotNull; 035 import org.opends.messages.Message; 036 import static org.opends.messages.CoreMessages.*; 037 038 import java.util.TreeMap; 039 import java.util.List; 040 import java.util.LinkedList; 041 import java.util.Map; 042 043 /** 044 * Registry for maintaining the set of registered base DN's, assocated 045 * backends and naming context information. 046 */ 047 public class BaseDnRegistry { 048 049 // The set of base DNs registered with the server. 050 private TreeMap<DN,Backend> baseDNs; 051 052 // The set of private naming contexts registered with the server. 053 private TreeMap<DN,Backend> privateNamingContexts; 054 055 // The set of public naming contexts registered with the server. 056 private TreeMap<DN,Backend> publicNamingContexts; 057 058 // Indicates whether or not this base DN registry is in test mode. 059 // A registry instance that is in test mode will not modify backend 060 // objects referred to in the above maps. 061 private boolean testOnly; 062 063 /** 064 * Registers a base DN with this registry. 065 * 066 * @param baseDN to register 067 * @param backend with which the base DN is assocated 068 * @param isPrivate indicates whether or not this base DN is private 069 * @return list of error messages generated by registering the base DN 070 * that should be logged if the changes to this registry are 071 * committed to the server 072 * @throws DirectoryException if the base DN cannot be registered 073 */ 074 public List<Message> registerBaseDN(DN baseDN, Backend backend, 075 boolean isPrivate) 076 throws DirectoryException 077 { 078 079 List<Message> errors = new LinkedList<Message>(); 080 081 // Check to see if the base DN is already registered with the server. 082 Backend existingBackend = baseDNs.get(baseDN); 083 if (existingBackend != null) 084 { 085 Message message = ERR_REGISTER_BASEDN_ALREADY_EXISTS. 086 get(String.valueOf(baseDN), backend.getBackendID(), 087 existingBackend.getBackendID()); 088 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 089 } 090 091 092 // Check to see if the backend is already registered with the server for 093 // any other base DN(s). The new base DN must not have any hierarchical 094 // relationship with any other base Dns for the same backend. 095 LinkedList<DN> otherBaseDNs = new LinkedList<DN>(); 096 for (DN dn : baseDNs.keySet()) 097 { 098 Backend b = baseDNs.get(dn); 099 if (b.equals(backend)) 100 { 101 otherBaseDNs.add(dn); 102 103 if (baseDN.isAncestorOf(dn) || baseDN.isDescendantOf(dn)) 104 { 105 Message message = ERR_REGISTER_BASEDN_HIERARCHY_CONFLICT. 106 get(String.valueOf(baseDN), backend.getBackendID(), 107 String.valueOf(dn)); 108 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 109 message); 110 } 111 } 112 } 113 114 115 // Check to see if the new base DN is subordinate to any other base DN 116 // already defined. If it is, then any other base DN(s) for the same 117 // backend must also be subordinate to the same base DN. 118 Backend superiorBackend = null; 119 DN superiorBaseDN ; 120 DN parentDN = baseDN.getParent(); 121 while (parentDN != null) 122 { 123 if (baseDNs.containsKey(parentDN)) 124 { 125 superiorBaseDN = parentDN; 126 superiorBackend = baseDNs.get(parentDN); 127 128 for (DN dn : otherBaseDNs) 129 { 130 if (! dn.isDescendantOf(superiorBaseDN)) 131 { 132 Message message = ERR_REGISTER_BASEDN_DIFFERENT_PARENT_BASES. 133 get(String.valueOf(baseDN), backend.getBackendID(), 134 String.valueOf(dn)); 135 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 136 message); 137 } 138 } 139 140 break; 141 } 142 143 parentDN = parentDN.getParent(); 144 } 145 146 if (superiorBackend == null) 147 { 148 if (backend.getParentBackend() != null) 149 { 150 Message message = ERR_REGISTER_BASEDN_NEW_BASE_NOT_SUBORDINATE. 151 get(String.valueOf(baseDN), backend.getBackendID(), 152 backend.getParentBackend().getBackendID()); 153 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 154 message); 155 } 156 } 157 158 159 // Check to see if the new base DN should be the superior base DN for any 160 // other base DN(s) already defined. 161 LinkedList<Backend> subordinateBackends = new LinkedList<Backend>(); 162 LinkedList<DN> subordinateBaseDNs = new LinkedList<DN>(); 163 for (DN dn : baseDNs.keySet()) 164 { 165 Backend b = baseDNs.get(dn); 166 parentDN = dn.getParent(); 167 while (parentDN != null) 168 { 169 if (parentDN.equals(baseDN)) 170 { 171 subordinateBaseDNs.add(dn); 172 subordinateBackends.add(b); 173 break; 174 } 175 else if (baseDNs.containsKey(parentDN)) 176 { 177 break; 178 } 179 180 parentDN = parentDN.getParent(); 181 } 182 } 183 184 185 // If we've gotten here, then the new base DN is acceptable. If we should 186 // actually apply the changes then do so now. 187 188 // Check to see if any of the registered backends already contain an 189 // entry with the DN specified as the base DN. This could happen if 190 // we're creating a new subordinate backend in an existing directory 191 // (e.g., moving the "ou=People,dc=example,dc=com" branch to its own 192 // backend when that data already exists under the "dc=example,dc=com" 193 // backend). This condition shouldn't prevent the new base DN from 194 // being registered, but it's definitely important enough that we let 195 // the administrator know about it and remind them that the existing 196 // backend will need to be reinitialized. 197 if (superiorBackend != null) 198 { 199 if (superiorBackend.entryExists(baseDN)) 200 { 201 Message message = WARN_REGISTER_BASEDN_ENTRIES_IN_MULTIPLE_BACKENDS. 202 get(superiorBackend.getBackendID(), String.valueOf(baseDN), 203 backend.getBackendID()); 204 errors.add(message); 205 } 206 } 207 208 209 baseDNs.put(baseDN, backend); 210 211 if (superiorBackend == null) 212 { 213 if (isPrivate) 214 { 215 if (!testOnly) 216 { 217 backend.setPrivateBackend(true); 218 } 219 privateNamingContexts.put(baseDN, backend); 220 } 221 else 222 { 223 if (!testOnly) 224 { 225 backend.setPrivateBackend(false); 226 } 227 publicNamingContexts.put(baseDN, backend); 228 } 229 } 230 else if (otherBaseDNs.isEmpty()) 231 { 232 if (!testOnly) 233 { 234 backend.setParentBackend(superiorBackend); 235 superiorBackend.addSubordinateBackend(backend); 236 } 237 } 238 239 if (!testOnly) 240 { 241 for (Backend b : subordinateBackends) 242 { 243 Backend oldParentBackend = b.getParentBackend(); 244 if (oldParentBackend != null) 245 { 246 oldParentBackend.removeSubordinateBackend(b); 247 } 248 249 b.setParentBackend(backend); 250 backend.addSubordinateBackend(b); 251 } 252 } 253 254 for (DN dn : subordinateBaseDNs) 255 { 256 publicNamingContexts.remove(dn); 257 privateNamingContexts.remove(dn); 258 } 259 260 return errors; 261 } 262 263 264 /** 265 * Deregisters a base DN with this registry. 266 * 267 * @param baseDN to deregister 268 * @return list of error messages generated by deregistering the base DN 269 * that should be logged if the changes to this registry are 270 * committed to the server 271 * @throws DirectoryException if the base DN could not be deregistered 272 */ 273 public List<Message> deregisterBaseDN(DN baseDN) 274 throws DirectoryException 275 { 276 LinkedList<Message> errors = new LinkedList<Message>(); 277 278 ensureNotNull(baseDN); 279 280 // Make sure that the Directory Server actually contains a backend with 281 // the specified base DN. 282 Backend backend = baseDNs.get(baseDN); 283 if (backend == null) 284 { 285 Message message = 286 ERR_DEREGISTER_BASEDN_NOT_REGISTERED.get(String.valueOf(baseDN)); 287 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 288 } 289 290 291 // Check to see if the backend has a parent backend, and whether it has 292 // any subordinates with base DNs that are below the base DN to remove. 293 Backend superiorBackend = backend.getParentBackend(); 294 LinkedList<Backend> subordinateBackends = new LinkedList<Backend>(); 295 if (backend.getSubordinateBackends() != null) 296 { 297 for (Backend b : backend.getSubordinateBackends()) 298 { 299 for (DN dn : b.getBaseDNs()) 300 { 301 if (dn.isDescendantOf(baseDN)) 302 { 303 subordinateBackends.add(b); 304 break; 305 } 306 } 307 } 308 } 309 310 311 // See if there are any other base DNs registered within the same backend. 312 LinkedList<DN> otherBaseDNs = new LinkedList<DN>(); 313 for (DN dn : baseDNs.keySet()) 314 { 315 if (dn.equals(baseDN)) 316 { 317 continue; 318 } 319 320 Backend b = baseDNs.get(dn); 321 if (backend.equals(b)) 322 { 323 otherBaseDNs.add(dn); 324 } 325 } 326 327 328 // If we've gotten here, then it's OK to make the changes. 329 330 // Get rid of the references to this base DN in the mapping tree 331 // information. 332 baseDNs.remove(baseDN); 333 publicNamingContexts.remove(baseDN); 334 privateNamingContexts.remove(baseDN); 335 336 if (superiorBackend == null) 337 { 338 // If there were any subordinate backends, then all of their base DNs 339 // will now be promoted to naming contexts. 340 for (Backend b : subordinateBackends) 341 { 342 if (!testOnly) 343 { 344 b.setParentBackend(null); 345 backend.removeSubordinateBackend(b); 346 } 347 348 for (DN dn : b.getBaseDNs()) 349 { 350 if (b.isPrivateBackend()) 351 { 352 privateNamingContexts.put(dn, b); 353 } 354 else 355 { 356 publicNamingContexts.put(dn, b); 357 } 358 } 359 } 360 } 361 else 362 { 363 // If there are no other base DNs for the associated backend, then 364 // remove this backend as a subordinate of the parent backend. 365 if (otherBaseDNs.isEmpty()) 366 { 367 if (!testOnly) 368 { 369 superiorBackend.removeSubordinateBackend(backend); 370 } 371 } 372 373 374 // If there are any subordinate backends, then they need to be made 375 // subordinate to the parent backend. Also, we should log a warning 376 // message indicating that there may be inconsistent search results 377 // because some of the structural entries will be missing. 378 if (! subordinateBackends.isEmpty()) 379 { 380 // Suppress this warning message on server shutdown. 381 if (!DirectoryServer.getInstance().isShuttingDown()) { 382 Message message = WARN_DEREGISTER_BASEDN_MISSING_HIERARCHY.get( 383 String.valueOf(baseDN), backend.getBackendID()); 384 errors.add(message); 385 } 386 387 if (!testOnly) 388 { 389 for (Backend b : subordinateBackends) 390 { 391 backend.removeSubordinateBackend(b); 392 superiorBackend.addSubordinateBackend(b); 393 b.setParentBackend(superiorBackend); 394 } 395 } 396 } 397 } 398 return errors; 399 } 400 401 402 /** 403 * Creates a default instance. 404 */ 405 BaseDnRegistry() 406 { 407 this(new TreeMap<DN,Backend>(), new TreeMap<DN,Backend>(), 408 new TreeMap<DN,Backend>(), false); 409 } 410 411 /** 412 * Returns a copy of this registry. 413 * 414 * @return copy of this registry 415 */ 416 BaseDnRegistry copy() 417 { 418 return new BaseDnRegistry( 419 new TreeMap<DN,Backend>(baseDNs), 420 new TreeMap<DN,Backend>(publicNamingContexts), 421 new TreeMap<DN,Backend>(privateNamingContexts), 422 true); 423 } 424 425 426 /** 427 * Creates a parameterized instance. 428 * 429 * @param baseDNs map 430 * @param publicNamingContexts map 431 * @param privateNamingContexts map 432 * @param testOnly indicates whether this registry will be used for testing; 433 * when <code>true</code> this registry will not modify backends 434 */ 435 private BaseDnRegistry(TreeMap<DN, Backend> baseDNs, 436 TreeMap<DN, Backend> publicNamingContexts, 437 TreeMap<DN, Backend> privateNamingContexts, 438 boolean testOnly) 439 { 440 this.baseDNs = baseDNs; 441 this.publicNamingContexts = publicNamingContexts; 442 this.privateNamingContexts = privateNamingContexts; 443 this.testOnly = testOnly; 444 } 445 446 447 /** 448 * Gets the mapping of registered base DNs to their associated backend. 449 * 450 * @return mapping from base DN to backend 451 */ 452 Map<DN,Backend> getBaseDnMap() { 453 return this.baseDNs; 454 } 455 456 457 /** 458 * Gets the mapping of registered public naming contexts to their 459 * associated backend. 460 * 461 * @return mapping from naming context to backend 462 */ 463 Map<DN,Backend> getPublicNamingContextsMap() { 464 return this.publicNamingContexts; 465 } 466 467 468 /** 469 * Gets the mapping of registered private naming contexts to their 470 * associated backend. 471 * 472 * @return mapping from naming context to backend 473 */ 474 Map<DN,Backend> getPrivateNamingContextsMap() { 475 return this.privateNamingContexts; 476 } 477 478 479 480 481 /** 482 * Indicates whether the specified DN is contained in this registry as 483 * a naming contexts. 484 * 485 * @param dn The DN for which to make the determination. 486 * 487 * @return {@code true} if the specified DN is a naming context in this 488 * registry, or {@code false} if it is not. 489 */ 490 boolean containsNamingContext(DN dn) 491 { 492 return (privateNamingContexts.containsKey(dn) || 493 publicNamingContexts.containsKey(dn)); 494 } 495 496 497 /** 498 * Clear and nullify this registry's internal state. 499 */ 500 void clear() { 501 502 if (baseDNs != null) 503 { 504 baseDNs.clear(); 505 } 506 507 if (privateNamingContexts != null) 508 { 509 privateNamingContexts.clear(); 510 } 511 512 if (publicNamingContexts != null) 513 { 514 publicNamingContexts.clear(); 515 } 516 517 } 518 519 }