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 }