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 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.types;
028
029
030
031 import java.io.BufferedReader;
032 import java.io.BufferedWriter;
033 import java.io.File;
034 import java.io.FileReader;
035 import java.io.FileWriter;
036 import java.io.IOException;
037 import java.util.Collections;
038 import java.util.HashMap;
039 import java.util.LinkedHashSet;
040 import java.util.LinkedList;
041 import java.util.List;
042 import java.util.Map;
043 import java.util.TreeSet;
044 import java.util.concurrent.ConcurrentHashMap;
045
046 import org.opends.messages.Message;
047 import org.opends.server.api.ApproximateMatchingRule;
048 import org.opends.server.api.AttributeSyntax;
049 import org.opends.server.api.EqualityMatchingRule;
050 import org.opends.server.api.MatchingRule;
051 import org.opends.server.api.OrderingMatchingRule;
052 import org.opends.server.api.SubstringMatchingRule;
053 import org.opends.server.core.DirectoryServer;
054 import org.opends.server.core.SchemaConfigManager;
055 import org.opends.server.loggers.debug.DebugTracer;
056 import org.opends.server.protocols.asn1.ASN1OctetString;
057 import org.opends.server.schema.CaseIgnoreEqualityMatchingRule;
058
059 import static org.opends.messages.BackendMessages.*;
060 import static org.opends.messages.CoreMessages.*;
061 import static org.opends.server.config.ConfigConstants.*;
062 import static org.opends.server.loggers.debug.DebugLogger.*;
063 import static org.opends.server.loggers.ErrorLogger.*;
064 import static org.opends.server.util.ServerConstants.*;
065 import static org.opends.server.util.StaticUtils.*;
066
067
068
069 /**
070 * This class defines a data structure that holds information about
071 * the components of the Directory Server schema. It includes the
072 * following kinds of elements:
073 *
074 * <UL>
075 * <LI>Attribute type definitions</LI>
076 * <LI>Objectclass definitions</LI>
077 * <LI>Attribute syntax definitions</LI>
078 * <LI>Matching rule definitions</LI>
079 * <LI>Matching rule use definitions</LI>
080 * <LI>DIT content rule definitions</LI>
081 * <LI>DIT structure rule definitions</LI>
082 * <LI>Name form definitions</LI>
083 * </UL>
084 */
085 @org.opends.server.types.PublicAPI(
086 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
087 mayInstantiate=false,
088 mayExtend=false,
089 mayInvoke=true)
090 public final class Schema
091 {
092 /**
093 * The tracer object for the debug logger.
094 */
095 private static final DebugTracer TRACER = getTracer();
096
097
098
099
100 // The matching rule that will be used to normalize schema element
101 // definitions.
102 private EqualityMatchingRule normalizationMatchingRule;
103
104 // The set of subordinate attribute types registered within the
105 // server schema.
106 private ConcurrentHashMap<AttributeType,List<AttributeType>>
107 subordinateTypes;
108
109 // The set of attribute type definitions for this schema, mapped
110 // between the lowercase names and OID for the definition and the
111 // attribute type itself.
112 private ConcurrentHashMap<String,AttributeType> attributeTypes;
113
114 // The set of objectclass definitions for this schema, mapped
115 // between the lowercase names and OID for the definition and the
116 // objectclass itself.
117 private ConcurrentHashMap<String,ObjectClass> objectClasses;
118
119 // The set of attribute syntaxes for this schema, mapped between the
120 // OID for the syntax and the syntax itself.
121 private ConcurrentHashMap<String,AttributeSyntax> syntaxes;
122
123 // The entire set of matching rules for this schema, mapped between
124 // the lowercase names and OID for the definition and the matching
125 // rule itself.
126 private ConcurrentHashMap<String,MatchingRule> matchingRules;
127
128 // The set of approximate matching rules for this schema, mapped
129 // between the lowercase names and OID for the definition and the
130 // matching rule itself.
131 private ConcurrentHashMap<String,ApproximateMatchingRule>
132 approximateMatchingRules;
133
134 // The set of equality matching rules for this schema, mapped
135 // between the lowercase names and OID for the definition and the
136 // matching rule itself.
137 private ConcurrentHashMap<String,EqualityMatchingRule>
138 equalityMatchingRules;
139
140 // The set of ordering matching rules for this schema, mapped
141 // between the lowercase names and OID for the definition and the
142 // matching rule itself.
143 private ConcurrentHashMap<String,OrderingMatchingRule>
144 orderingMatchingRules;
145
146 // The set of substring matching rules for this schema, mapped
147 // between the lowercase names and OID for the definition and the
148 // matching rule itself.
149 private ConcurrentHashMap<String,SubstringMatchingRule>
150 substringMatchingRules;
151
152 // The set of matching rule uses for this schema, mapped between the
153 // matching rule for the definition and the matching rule use
154 // itself.
155 private ConcurrentHashMap<MatchingRule,MatchingRuleUse>
156 matchingRuleUses;
157
158 // The set of DIT content rules for this schema, mapped between the
159 // structural objectclass for the definition and the DIT content
160 // rule itself.
161 private ConcurrentHashMap<ObjectClass,DITContentRule>
162 ditContentRules;
163
164 // The set of DIT structure rules for this schema, mapped between
165 // the name form for the definition and the DIT structure rule
166 // itself.
167 private ConcurrentHashMap<Integer,DITStructureRule>
168 ditStructureRulesByID;
169
170 // The set of DIT structure rules for this schema, mapped between
171 // the name form for the definition and the DIT structure rule
172 // itself.
173 private ConcurrentHashMap<NameForm,DITStructureRule>
174 ditStructureRulesByNameForm;
175
176 // The set of name forms for this schema, mapped between the
177 // structural objectclass for the definition and the name form
178 // itself.
179 private ConcurrentHashMap<ObjectClass,NameForm> nameFormsByOC;
180
181 // The set of name forms for this schema, mapped between the
182 // names/OID and the name form itself.
183 private ConcurrentHashMap<String,NameForm> nameFormsByName;
184
185 // The set of pre-encoded attribute syntax representations.
186 private LinkedHashSet<AttributeValue> syntaxSet;
187
188 // The set of pre-encoded attribute type representations.
189 private LinkedHashSet<AttributeValue> attributeTypeSet;
190
191 // The set of pre-encoded DIT content rule representations.
192 private LinkedHashSet<AttributeValue> ditContentRuleSet;
193
194 // The set of pre-encoded DIT structure rule representations.
195 private LinkedHashSet<AttributeValue> ditStructureRuleSet;
196
197 // The set of pre-encoded matching rule representations.
198 private LinkedHashSet<AttributeValue> matchingRuleSet;
199
200 // The set of pre-encoded matching rule use representations.
201 private LinkedHashSet<AttributeValue> matchingRuleUseSet;
202
203 // The set of pre-encoded name form representations.
204 private LinkedHashSet<AttributeValue> nameFormSet;
205
206 // The set of pre-encoded objectclass representations.
207 private LinkedHashSet<AttributeValue> objectClassSet;
208
209 // The oldest modification timestamp for any schema configuration
210 // file.
211 private long oldestModificationTime;
212
213 // The youngest modification timestamp for any schema configuration
214 // file.
215 private long youngestModificationTime;
216
217 // A set of extra attributes that are not used directly by
218 // the schema but may be used by other component to store
219 // information in the schema.
220 // ex : Replication uses this to store its state and GenerationID.
221
222 private Map<String, Attribute> extraAttributes =
223 new HashMap<String, Attribute>();
224
225
226
227 /**
228 * Creates a new schema structure with all elements initialized but
229 * empty.
230 */
231 public Schema()
232 {
233 attributeTypes = new ConcurrentHashMap<String,AttributeType>();
234 objectClasses = new ConcurrentHashMap<String,ObjectClass>();
235 syntaxes = new ConcurrentHashMap<String,AttributeSyntax>();
236 matchingRules = new ConcurrentHashMap<String,MatchingRule>();
237 approximateMatchingRules =
238 new ConcurrentHashMap<String,ApproximateMatchingRule>();
239 equalityMatchingRules =
240 new ConcurrentHashMap<String,EqualityMatchingRule>();
241 orderingMatchingRules =
242 new ConcurrentHashMap<String,OrderingMatchingRule>();
243 substringMatchingRules =
244 new ConcurrentHashMap<String,SubstringMatchingRule>();
245 matchingRuleUses =
246 new ConcurrentHashMap<MatchingRule,MatchingRuleUse>();
247 ditContentRules =
248 new ConcurrentHashMap<ObjectClass,DITContentRule>();
249 ditStructureRulesByID =
250 new ConcurrentHashMap<Integer,DITStructureRule>();
251 ditStructureRulesByNameForm =
252 new ConcurrentHashMap<NameForm,DITStructureRule>();
253 nameFormsByOC = new ConcurrentHashMap<ObjectClass,NameForm>();
254 nameFormsByName = new ConcurrentHashMap<String,NameForm>();
255 subordinateTypes =
256 new ConcurrentHashMap<AttributeType,List<AttributeType>>();
257
258
259 syntaxSet = new LinkedHashSet<AttributeValue>();
260 attributeTypeSet = new LinkedHashSet<AttributeValue>();
261 ditContentRuleSet = new LinkedHashSet<AttributeValue>();
262 ditStructureRuleSet = new LinkedHashSet<AttributeValue>();
263 matchingRuleSet = new LinkedHashSet<AttributeValue>();
264 matchingRuleUseSet = new LinkedHashSet<AttributeValue>();
265 nameFormSet = new LinkedHashSet<AttributeValue>();
266 objectClassSet = new LinkedHashSet<AttributeValue>();
267
268 normalizationMatchingRule = new CaseIgnoreEqualityMatchingRule();
269 oldestModificationTime = System.currentTimeMillis();
270 youngestModificationTime = oldestModificationTime;
271 }
272
273
274
275 /**
276 * Retrieves the attribute type definitions for this schema, as a
277 * mapping between the lowercase names and OIDs for the attribute
278 * type and the attribute type itself. Each attribute type may be
279 * associated with multiple keys (once for the OID and again for
280 * each name). The contents of the returned mapping must not be
281 * altered.
282 *
283 * @return The attribute type definitions for this schema.
284 */
285 public ConcurrentHashMap<String,AttributeType> getAttributeTypes()
286 {
287 return attributeTypes;
288 }
289
290
291
292 /**
293 * Retrieves the set of defined attribute types for this schema.
294 *
295 * @return The set of defined attribute types for this schema.
296 */
297 public LinkedHashSet<AttributeValue> getAttributeTypeSet()
298 {
299 return attributeTypeSet;
300 }
301
302
303
304 /**
305 * Indicates whether this schema definition includes an attribute
306 * type with the provided name or OID.
307 *
308 * @param lowerName The name or OID for which to make the
309 * determination, formatted in all lowercase
310 * characters.
311 *
312 * @return {@code true} if this schema contains an attribute type
313 * with the provided name or OID, or {@code false} if not.
314 */
315 public boolean hasAttributeType(String lowerName)
316 {
317 return attributeTypes.containsKey(lowerName);
318 }
319
320
321
322 /**
323 * Retrieves the attribute type definition with the specified name
324 * or OID.
325 *
326 * @param lowerName The name or OID of the attribute type to
327 * retrieve, formatted in all lowercase
328 * characters.
329 *
330 * @return The requested attribute type, or <CODE>null</CODE> if no
331 * type is registered with the provided name or OID.
332 */
333 public AttributeType getAttributeType(String lowerName)
334 {
335 return attributeTypes.get(lowerName);
336 }
337
338
339
340 /**
341 * Registers the provided attribute type definition with this
342 * schema.
343 *
344 * @param attributeType The attribute type to register with
345 * this schema.
346 * @param overwriteExisting Indicates whether to overwrite an
347 * existing mapping if there are any
348 * conflicts (i.e., another attribute
349 * type with the same OID or name).
350 *
351 * @throws DirectoryException If a conflict is encountered and the
352 * <CODE>overwriteExisting</CODE> flag
353 * is set to <CODE>false</CODE>
354 */
355 public void registerAttributeType(AttributeType attributeType,
356 boolean overwriteExisting)
357 throws DirectoryException
358 {
359 synchronized (attributeTypes)
360 {
361 if (! overwriteExisting)
362 {
363 String oid = toLowerCase(attributeType.getOID());
364 if (attributeTypes.containsKey(oid))
365 {
366 AttributeType conflictingType = attributeTypes.get(oid);
367
368 Message message = ERR_SCHEMA_CONFLICTING_ATTRIBUTE_OID.
369 get(attributeType.getNameOrOID(), oid,
370 conflictingType.getNameOrOID());
371 throw new DirectoryException(
372 ResultCode.CONSTRAINT_VIOLATION, message);
373 }
374
375 for (String name : attributeType.getNormalizedNames())
376 {
377 if (attributeTypes.containsKey(name))
378 {
379 AttributeType conflictingType = attributeTypes.get(name);
380
381 Message message = ERR_SCHEMA_CONFLICTING_ATTRIBUTE_NAME.
382 get(attributeType.getNameOrOID(), name,
383 conflictingType.getNameOrOID());
384 throw new DirectoryException(
385 ResultCode.CONSTRAINT_VIOLATION, message);
386 }
387 }
388 }
389
390 attributeTypes.put(toLowerCase(attributeType.getOID()),
391 attributeType);
392
393 for (String name : attributeType.getNormalizedNames())
394 {
395 attributeTypes.put(name, attributeType);
396 }
397
398 AttributeType superiorType = attributeType.getSuperiorType();
399 if (superiorType != null)
400 {
401 registerSubordinateType(attributeType, superiorType);
402 }
403
404 // We'll use an attribute value including the normalized value
405 // rather than the attribute type because otherwise it would use
406 // a very expensive matching rule (OID first component match)
407 // that would kill performance.
408 String valueString = attributeType.getDefinition();
409 ASN1OctetString rawValue = new ASN1OctetString(valueString);
410 ByteString normValue = normalizationMatchingRule.normalizeValue(
411 new ASN1OctetString(valueString));
412 attributeTypeSet.add(new AttributeValue(rawValue, normValue));
413 }
414 }
415
416
417
418 /**
419 * Deregisters the provided attribute type definition with this
420 * schema.
421 *
422 * @param attributeType The attribute type to deregister with this
423 * schema.
424 */
425 public void deregisterAttributeType(AttributeType attributeType)
426 {
427 synchronized (attributeTypes)
428 {
429 attributeTypes.remove(toLowerCase(attributeType.getOID()),
430 attributeType);
431
432 for (String name : attributeType.getNormalizedNames())
433 {
434 attributeTypes.remove(name, attributeType);
435 }
436
437 AttributeType superiorType = attributeType.getSuperiorType();
438 if (superiorType != null)
439 {
440 deregisterSubordinateType(attributeType, superiorType);
441 }
442
443 // We'll use an attribute value including the normalized value
444 // rather than the attribute type because otherwise it would use
445 // a very expensive matching rule (OID first component match)
446 // that would kill performance.
447 try
448 {
449 String valueString = attributeType.getDefinition();
450 ASN1OctetString rawValue = new ASN1OctetString(valueString);
451 ByteString normValue =
452 normalizationMatchingRule.normalizeValue(
453 new ASN1OctetString(valueString));
454 attributeTypeSet.remove(new AttributeValue(rawValue,
455 normValue));
456 }
457 catch (Exception e)
458 {
459 String valueString = attributeType.getDefinition();
460 ASN1OctetString rawValue = new ASN1OctetString(valueString);
461 ASN1OctetString normValue =
462 new ASN1OctetString(toLowerCase(valueString));
463 attributeTypeSet.remove(new AttributeValue(rawValue,
464 normValue));
465 }
466 }
467 }
468
469
470
471 /**
472 * Registers the provided attribute type as a subtype of the given
473 * superior attribute type, recursively following any additional
474 * elements in the superior chain.
475 *
476 * @param attributeType The attribute type to be registered as a
477 * subtype for the given superior type.
478 * @param superiorType The superior type for which to register
479 * the given attribute type as a subtype.
480 */
481 private void registerSubordinateType(AttributeType attributeType,
482 AttributeType superiorType)
483 {
484 List<AttributeType> subTypes = subordinateTypes.get(superiorType);
485 if (subTypes == null)
486 {
487 superiorType.setMayHaveSubordinateTypes();
488 subTypes = new LinkedList<AttributeType>();
489 subTypes.add(attributeType);
490 subordinateTypes.put(superiorType, subTypes);
491 }
492 else if (! subTypes.contains(attributeType))
493 {
494 superiorType.setMayHaveSubordinateTypes();
495 subTypes.add(attributeType);
496
497 AttributeType higherSuperior = superiorType.getSuperiorType();
498 if (higherSuperior != null)
499 {
500 registerSubordinateType(attributeType, higherSuperior);
501 }
502 }
503 }
504
505
506
507 /**
508 * Deregisters the provided attribute type as a subtype of the given
509 * superior attribute type, recursively following any additional
510 * elements in the superior chain.
511 *
512 * @param attributeType The attribute type to be deregistered as a
513 * subtype for the given superior type.
514 * @param superiorType The superior type for which to deregister
515 * the given attribute type as a subtype.
516 */
517 private void deregisterSubordinateType(AttributeType attributeType,
518 AttributeType superiorType)
519 {
520 List<AttributeType> subTypes = subordinateTypes.get(superiorType);
521 if (subTypes != null)
522 {
523 if (subTypes.remove(attributeType))
524 {
525 AttributeType higherSuperior = superiorType.getSuperiorType();
526 if (higherSuperior != null)
527 {
528 deregisterSubordinateType(attributeType, higherSuperior);
529 }
530 }
531 }
532 }
533
534
535
536 /**
537 * Retrieves the set of subtypes registered for the given attribute
538 * type.
539 *
540 * @param attributeType The attribute type for which to retrieve
541 * the set of registered subtypes.
542 *
543 * @return The set of subtypes registered for the given attribute
544 * type, or an empty set if there are no subtypes
545 * registered for the attribute type.
546 */
547 public Iterable<AttributeType>
548 getSubTypes(AttributeType attributeType)
549 {
550 List<AttributeType> subTypes =
551 subordinateTypes.get(attributeType);
552 if (subTypes == null)
553 {
554 return Collections.<AttributeType>emptyList();
555 }
556 else
557 {
558 return subTypes;
559 }
560 }
561
562
563
564 /**
565 * Retrieves the objectclass definitions for this schema, as a
566 * mapping between the lowercase names and OIDs for the objectclass
567 * and the objectclass itself. Each objectclass may be associated
568 * with multiple keys (once for the OID and again for each name).
569 * The contents of the returned mapping must not be altered.
570 *
571 * @return The objectclass definitions for this schema.
572 */
573 public ConcurrentHashMap<String,ObjectClass> getObjectClasses()
574 {
575 return objectClasses;
576 }
577
578
579
580 /**
581 * Retrieves the set of defined objectclasses for this schema.
582 *
583 * @return The set of defined objectclasses for this schema.
584 */
585 public LinkedHashSet<AttributeValue> getObjectClassSet()
586 {
587 return objectClassSet;
588 }
589
590
591
592 /**
593 * Indicates whether this schema definition includes an objectclass
594 * with the provided name or OID.
595 *
596 * @param lowerName The name or OID for which to make the
597 * determination, formatted in all lowercase
598 * characters.
599 *
600 * @return {@code true} if this schema contains an objectclass with
601 * the provided name or OID, or {@code false} if not.
602 */
603 public boolean hasObjectClass(String lowerName)
604 {
605 return objectClasses.containsKey(lowerName);
606 }
607
608
609
610 /**
611 * Retrieves the objectclass definition with the specified name or
612 * OID.
613 *
614 * @param lowerName The name or OID of the objectclass to
615 * retrieve, formatted in all lowercase
616 * characters.
617 *
618 * @return The requested objectclass, or <CODE>null</CODE> if no
619 * class is registered with the provided name or OID.
620 */
621 public ObjectClass getObjectClass(String lowerName)
622 {
623 return objectClasses.get(lowerName);
624 }
625
626
627
628 /**
629 * Registers the provided objectclass definition with this schema.
630 *
631 * @param objectClass The objectclass to register with this
632 * schema.
633 * @param overwriteExisting Indicates whether to overwrite an
634 * existing mapping if there are any
635 * conflicts (i.e., another objectclass
636 * with the same OID or name).
637 *
638 * @throws DirectoryException If a conflict is encountered and the
639 * <CODE>overwriteExisting</CODE> flag
640 * is set to <CODE>false</CODE>.
641 */
642 public void registerObjectClass(ObjectClass objectClass,
643 boolean overwriteExisting)
644 throws DirectoryException
645 {
646 synchronized (objectClasses)
647 {
648 if (! overwriteExisting)
649 {
650 String oid = toLowerCase(objectClass.getOID());
651 if (objectClasses.containsKey(oid))
652 {
653 ObjectClass conflictingClass = objectClasses.get(oid);
654
655 Message message = ERR_SCHEMA_CONFLICTING_OBJECTCLASS_OID.
656 get(objectClass.getNameOrOID(), oid,
657 conflictingClass.getNameOrOID());
658 throw new DirectoryException(
659 ResultCode.CONSTRAINT_VIOLATION, message);
660 }
661
662 for (String name : objectClass.getNormalizedNames())
663 {
664 if (objectClasses.containsKey(name))
665 {
666 ObjectClass conflictingClass = objectClasses.get(name);
667
668 Message message = ERR_SCHEMA_CONFLICTING_OBJECTCLASS_NAME.
669 get(objectClass.getNameOrOID(), name,
670 conflictingClass.getNameOrOID());
671 throw new DirectoryException(
672 ResultCode.CONSTRAINT_VIOLATION, message);
673 }
674 }
675 }
676
677 objectClasses.put(toLowerCase(objectClass.getOID()),
678 objectClass);
679
680 for (String name : objectClass.getNormalizedNames())
681 {
682 objectClasses.put(name, objectClass);
683 }
684
685 // We'll use an attribute value including the normalized value
686 // rather than the attribute type because otherwise it would use
687 // a very expensive matching rule (OID first component match)
688 // that would kill performance.
689 String valueString = objectClass.getDefinition();
690 ASN1OctetString rawValue = new ASN1OctetString(valueString);
691 ByteString normValue = normalizationMatchingRule.normalizeValue(
692 new ASN1OctetString(valueString));
693 objectClassSet.add(new AttributeValue(rawValue, normValue));
694 }
695 }
696
697
698
699 /**
700 * Deregisters the provided objectclass definition with this schema.
701 *
702 * @param objectClass The objectclass to deregister with this
703 * schema.
704 */
705 public void deregisterObjectClass(ObjectClass objectClass)
706 {
707 synchronized (objectClasses)
708 {
709 objectClasses.remove(toLowerCase(objectClass.getOID()),
710 objectClass);
711
712 for (String name : objectClass.getNormalizedNames())
713 {
714 objectClasses.remove(name, objectClass);
715 }
716
717
718 // We'll use an attribute value including the normalized value
719 // rather than the attribute type because otherwise it would use
720 // a very expensive matching rule (OID first component match)
721 // that would kill performance.
722 try
723 {
724 String valueString = objectClass.getDefinition();
725 ASN1OctetString rawValue = new ASN1OctetString(valueString);
726 ByteString normValue =
727 normalizationMatchingRule.normalizeValue(
728 new ASN1OctetString(valueString));
729 objectClassSet.remove(new AttributeValue(rawValue,
730 normValue));
731 }
732 catch (Exception e)
733 {
734 String valueString = objectClass.getDefinition();
735 ASN1OctetString rawValue = new ASN1OctetString(valueString);
736 ASN1OctetString normValue =
737 new ASN1OctetString(toLowerCase(valueString));
738 objectClassSet.remove(new AttributeValue(rawValue,
739 normValue));
740 }
741 }
742 }
743
744
745
746 /**
747 * Retrieves the attribute syntax definitions for this schema, as a
748 * mapping between the OID for the syntax and the syntax itself.
749 * Each syntax should only be present once, since its only key is
750 * its OID. The contents of the returned mapping must not be
751 * altered.
752 *
753 * @return The attribute syntax definitions for this schema.
754 */
755 public ConcurrentHashMap<String,AttributeSyntax> getSyntaxes()
756 {
757 return syntaxes;
758 }
759
760
761
762 /**
763 * Retrieves the set of defined attribute syntaxes for this schema.
764 *
765 * @return The set of defined attribute syntaxes for this schema.
766 */
767 public LinkedHashSet<AttributeValue> getSyntaxSet()
768 {
769 return syntaxSet;
770 }
771
772
773
774 /**
775 * Indicates whether this schema definition includes an attribute
776 * syntax with the provided name or OID.
777 *
778 * @param lowerName The name or OID for which to make the
779 * determination, formatted in all lowercase
780 * characters.
781 *
782 * @return {@code true} if this schema contains an attribute syntax
783 * with the provided name or OID, or {@code false} if not.
784 */
785 public boolean hasSyntax(String lowerName)
786 {
787 return syntaxes.containsKey(lowerName);
788 }
789
790
791
792 /**
793 * Retrieves the attribute syntax definition with the OID.
794 *
795 * @param lowerName The OID of the attribute syntax to retrieve,
796 * formatted in all lowercase characters.
797 *
798 * @return The requested attribute syntax, or <CODE>null</CODE> if
799 * no syntax is registered with the provided OID.
800 */
801 public AttributeSyntax getSyntax(String lowerName)
802 {
803 return syntaxes.get(lowerName);
804 }
805
806
807
808 /**
809 * Registers the provided attribute syntax definition with this
810 * schema.
811 *
812 * @param syntax The attribute syntax to register with
813 * this schema.
814 * @param overwriteExisting Indicates whether to overwrite an
815 * existing mapping if there are any
816 * conflicts (i.e., another attribute
817 * syntax with the same OID).
818 *
819 * @throws DirectoryException If a conflict is encountered and the
820 * <CODE>overwriteExisting</CODE> flag
821 * is set to <CODE>false</CODE>
822 */
823 public void registerSyntax(AttributeSyntax syntax,
824 boolean overwriteExisting)
825 throws DirectoryException
826 {
827 synchronized (syntaxes)
828 {
829 if (! overwriteExisting)
830 {
831 String oid = toLowerCase(syntax.getOID());
832 if (syntaxes.containsKey(oid))
833 {
834 AttributeSyntax conflictingSyntax = syntaxes.get(oid);
835
836 Message message = ERR_SCHEMA_CONFLICTING_SYNTAX_OID.
837 get(syntax.getSyntaxName(), oid,
838 conflictingSyntax.getSyntaxName());
839 throw new DirectoryException(
840 ResultCode.CONSTRAINT_VIOLATION, message);
841 }
842 }
843
844 syntaxes.put(toLowerCase(syntax.getOID()), syntax);
845
846 // We'll use an attribute value including the normalized value
847 // rather than the attribute type because otherwise it would use
848 // a very expensive matching rule (OID first component match)
849 // that would kill performance.
850 String valueString = syntax.toString();
851 ASN1OctetString rawValue = new ASN1OctetString(valueString);
852 ByteString normValue = normalizationMatchingRule.normalizeValue(
853 new ASN1OctetString(valueString));
854 syntaxSet.add(new AttributeValue(rawValue, normValue));
855 }
856 }
857
858
859
860 /**
861 * Deregisters the provided attribute syntax definition with this
862 * schema.
863 *
864 * @param syntax The attribute syntax to deregister with this
865 * schema.
866 */
867 public void deregisterSyntax(AttributeSyntax syntax)
868 {
869 synchronized (syntaxes)
870 {
871 syntaxes.remove(toLowerCase(syntax.getOID()), syntax);
872
873 // We'll use an attribute value including the normalized value
874 // rather than the attribute type because otherwise it would use
875 // a very expensive matching rule (OID first component match)
876 // that would kill performance.
877 try
878 {
879 String valueString = syntax.toString();
880 ASN1OctetString rawValue = new ASN1OctetString(valueString);
881 ByteString normValue =
882 normalizationMatchingRule.normalizeValue(
883 new ASN1OctetString(valueString));
884 syntaxSet.remove(new AttributeValue(rawValue, normValue));
885 }
886 catch (Exception e)
887 {
888 String valueString = syntax.toString();
889 ASN1OctetString rawValue = new ASN1OctetString(valueString);
890 ASN1OctetString normValue =
891 new ASN1OctetString(toLowerCase(valueString));
892 syntaxSet.remove(new AttributeValue(rawValue, normValue));
893 }
894 }
895 }
896
897
898
899 /**
900 * Retrieves the entire set of matching rule definitions for this
901 * schema, as a mapping between the lowercase names and OIDs for the
902 * matching rule and the matching rule itself. Each matching rule
903 * may be associated with multiple keys (once for the OID and again
904 * for each name). This should be a superset of the sets of
905 * approximate, equality, ordering, and substring matching rules.
906 * The contents of the returned mapping must not be altered.
907 *
908 * @return The matching rule definitions for this schema.
909 */
910 public ConcurrentHashMap<String,MatchingRule> getMatchingRules()
911 {
912 return matchingRules;
913 }
914
915
916
917 /**
918 * Retrieves the set of defined matching rules for this schema.
919 *
920 * @return The set of defined matching rules for this schema.
921 */
922 public LinkedHashSet<AttributeValue> getMatchingRuleSet()
923 {
924 return matchingRuleSet;
925 }
926
927
928
929 /**
930 * Indicates whether this schema definition includes a matching rule
931 * with the provided name or OID.
932 *
933 * @param lowerName The name or OID for which to make the
934 * determination, formatted in all lowercase
935 * characters.
936 *
937 * @return {@code true} if this schema contains a matching rule
938 * with the provided name or OID, or {@code false} if not.
939 */
940 public boolean hasMatchingRule(String lowerName)
941 {
942 return matchingRules.containsKey(lowerName);
943 }
944
945
946
947 /**
948 * Retrieves the matching rule definition with the specified name or
949 * OID.
950 *
951 * @param lowerName The name or OID of the matching rule to
952 * retrieve, formatted in all lowercase
953 * characters.
954 *
955 * @return The requested matching rule, or <CODE>null</CODE> if no
956 * rule is registered with the provided name or OID.
957 */
958 public MatchingRule getMatchingRule(String lowerName)
959 {
960 return matchingRules.get(lowerName);
961 }
962
963
964
965 /**
966 * Registers the provided matching rule definition with this schema.
967 *
968 * @param matchingRule The matching rule to register with
969 * this schema.
970 * @param overwriteExisting Indicates whether to overwrite an
971 * existing mapping if there are any
972 * conflicts (i.e.,
973 * another matching rule with the same
974 * OID or name).
975 *
976 * @throws DirectoryException If a conflict is encountered and the
977 * <CODE>overwriteExisting</CODE> flag
978 * is set to <CODE>false</CODE>
979 */
980 public void registerMatchingRule(MatchingRule matchingRule,
981 boolean overwriteExisting)
982 throws DirectoryException
983 {
984 if (matchingRule instanceof ApproximateMatchingRule)
985 {
986 registerApproximateMatchingRule(
987 (ApproximateMatchingRule) matchingRule, overwriteExisting);
988 }
989 else if (matchingRule instanceof EqualityMatchingRule)
990 {
991 registerEqualityMatchingRule(
992 (EqualityMatchingRule) matchingRule, overwriteExisting);
993 }
994 else if (matchingRule instanceof OrderingMatchingRule)
995 {
996 registerOrderingMatchingRule(
997 (OrderingMatchingRule) matchingRule, overwriteExisting);
998 }
999 else if (matchingRule instanceof SubstringMatchingRule)
1000 {
1001 registerSubstringMatchingRule(
1002 (SubstringMatchingRule) matchingRule, overwriteExisting);
1003 }
1004 else
1005 {
1006 synchronized (matchingRules)
1007 {
1008 if (! overwriteExisting)
1009 {
1010 String oid = toLowerCase(matchingRule.getOID());
1011 if (matchingRules.containsKey(oid))
1012 {
1013 MatchingRule conflictingRule = matchingRules.get(oid);
1014
1015 Message message = ERR_SCHEMA_CONFLICTING_MR_OID.
1016 get(matchingRule.getNameOrOID(), oid,
1017 conflictingRule.getNameOrOID());
1018 throw new DirectoryException(
1019 ResultCode.CONSTRAINT_VIOLATION, message);
1020 }
1021
1022 String name = matchingRule.getName();
1023 if (name != null)
1024 {
1025 name = toLowerCase(name);
1026 if (matchingRules.containsKey(name))
1027 {
1028 MatchingRule conflictingRule = matchingRules.get(name);
1029
1030 Message message = ERR_SCHEMA_CONFLICTING_MR_NAME.
1031 get(matchingRule.getOID(), name,
1032 conflictingRule.getOID());
1033 throw new DirectoryException(
1034 ResultCode.CONSTRAINT_VIOLATION, message);
1035 }
1036 }
1037 }
1038
1039 matchingRules.put(toLowerCase(matchingRule.getOID()),
1040 matchingRule);
1041
1042 String name = matchingRule.getName();
1043 if (name != null)
1044 {
1045 matchingRules.put(toLowerCase(name), matchingRule);
1046 }
1047
1048 // We'll use an attribute value including the normalized value
1049 // rather than the attribute type because otherwise it would
1050 // use a very expensive matching rule (OID first component
1051 // match) that would kill performance.
1052 String valueString = matchingRule.toString();
1053 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1054 ByteString normValue =
1055 normalizationMatchingRule.normalizeValue(
1056 new ASN1OctetString(valueString));
1057 matchingRuleSet.add(new AttributeValue(rawValue, normValue));
1058 }
1059 }
1060 }
1061
1062
1063
1064 /**
1065 * Deregisters the provided matching rule definition with this
1066 * schema.
1067 *
1068 * @param matchingRule The matching rule to deregister with this
1069 * schema.
1070 */
1071 public void deregisterMatchingRule(MatchingRule matchingRule)
1072 {
1073 if (matchingRule instanceof ApproximateMatchingRule)
1074 {
1075 deregisterApproximateMatchingRule(
1076 (ApproximateMatchingRule) matchingRule);
1077 }
1078 else if (matchingRule instanceof EqualityMatchingRule)
1079 {
1080 deregisterEqualityMatchingRule(
1081 (EqualityMatchingRule) matchingRule);
1082 }
1083 else if (matchingRule instanceof OrderingMatchingRule)
1084 {
1085 deregisterOrderingMatchingRule(
1086 (OrderingMatchingRule) matchingRule);
1087 }
1088 else if (matchingRule instanceof SubstringMatchingRule)
1089 {
1090 deregisterSubstringMatchingRule(
1091 (SubstringMatchingRule) matchingRule);
1092 }
1093 else
1094 {
1095 synchronized (matchingRules)
1096 {
1097 matchingRules.remove(toLowerCase(matchingRule.getOID()),
1098 matchingRule);
1099
1100 String name = matchingRule.getName();
1101 if (name != null)
1102 {
1103 matchingRules.remove(toLowerCase(name), matchingRule);
1104 }
1105
1106
1107 // We'll use an attribute value including the normalized value
1108 // rather than the attribute type because otherwise it would
1109 // use a very expensive matching rule (OID first component
1110 // match) that would kill performance.
1111 try
1112 {
1113 String valueString = matchingRule.toString();
1114 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1115 ByteString normValue =
1116 normalizationMatchingRule.normalizeValue(
1117 new ASN1OctetString(valueString));
1118 matchingRuleSet.remove(new AttributeValue(rawValue,
1119 normValue));
1120 }
1121 catch (Exception e)
1122 {
1123 String valueString = matchingRule.toString();
1124 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1125 ASN1OctetString normValue =
1126 new ASN1OctetString(toLowerCase(valueString));
1127 matchingRuleSet.remove(new AttributeValue(rawValue,
1128 normValue));
1129 }
1130 }
1131 }
1132 }
1133
1134
1135
1136 /**
1137 * Retrieves the approximate matching rule definitions for this
1138 * schema, as a mapping between the lowercase names and OIDs for the
1139 * matching rule and the matching rule itself. Each matching rule
1140 * may be associated with multiple keys (once for the OID and again
1141 * for each name). The contents of the returned mapping must not be
1142 * altered.
1143 *
1144 * @return The approximate matching rule definitions for this
1145 * schema.
1146 */
1147 public ConcurrentHashMap<String,ApproximateMatchingRule>
1148 getApproximateMatchingRules()
1149 {
1150 return approximateMatchingRules;
1151 }
1152
1153
1154
1155 /**
1156 * Retrieves the approximate matching rule definition with the
1157 * specified name or OID.
1158 *
1159 * @param lowerName The name or OID of the matching rule to
1160 * retrieve, formatted in all lowercase
1161 * characters.
1162 *
1163 * @return The requested matching rule, or <CODE>null</CODE> if no
1164 * approximate matching rule is registered with the
1165 * provided name or OID.
1166 */
1167 public ApproximateMatchingRule getApproximateMatchingRule(
1168 String lowerName)
1169 {
1170 return approximateMatchingRules.get(lowerName);
1171 }
1172
1173
1174
1175 /**
1176 * Registers the provided approximate matching rule with this
1177 * schema.
1178 *
1179 * @param matchingRule The approximate matching rule to
1180 * register.
1181 * @param overwriteExisting Indicates whether to overwrite an
1182 * existing mapping if there are any
1183 * conflicts (i.e., another matching rule
1184 * with the same OID or name).
1185 *
1186 * @throws DirectoryException If a conflict is encountered and the
1187 * <CODE>overwriteExisting</CODE> flag
1188 * is set to <CODE>false</CODE>
1189 */
1190 public void registerApproximateMatchingRule(
1191 ApproximateMatchingRule matchingRule,
1192 boolean overwriteExisting)
1193 throws DirectoryException
1194 {
1195 synchronized (matchingRules)
1196 {
1197 if (! overwriteExisting)
1198 {
1199 String oid = toLowerCase(matchingRule.getOID());
1200 if (matchingRules.containsKey(oid))
1201 {
1202 MatchingRule conflictingRule = matchingRules.get(oid);
1203
1204 Message message = ERR_SCHEMA_CONFLICTING_MR_OID.
1205 get(matchingRule.getNameOrOID(), oid,
1206 conflictingRule.getNameOrOID());
1207 throw new DirectoryException(
1208 ResultCode.CONSTRAINT_VIOLATION, message);
1209 }
1210
1211 String name = matchingRule.getName();
1212 if (name != null)
1213 {
1214 name = toLowerCase(name);
1215 if (matchingRules.containsKey(name))
1216 {
1217 MatchingRule conflictingRule = matchingRules.get(name);
1218
1219 Message message = ERR_SCHEMA_CONFLICTING_MR_NAME.
1220 get(matchingRule.getOID(), name,
1221 conflictingRule.getOID());
1222 throw new DirectoryException(
1223 ResultCode.CONSTRAINT_VIOLATION, message);
1224 }
1225 }
1226 }
1227
1228 String oid = toLowerCase(matchingRule.getOID());
1229 approximateMatchingRules.put(oid, matchingRule);
1230 matchingRules.put(oid, matchingRule);
1231
1232 String name = matchingRule.getName();
1233 if (name != null)
1234 {
1235 name = toLowerCase(name);
1236 approximateMatchingRules.put(name, matchingRule);
1237 matchingRules.put(name, matchingRule);
1238 }
1239
1240 // We'll use an attribute value including the normalized value
1241 // rather than the attribute type because otherwise it would use
1242 // a very expensive matching rule (OID first component match)
1243 // that would kill performance.
1244 String valueString = matchingRule.toString();
1245 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1246 ByteString normValue = normalizationMatchingRule.normalizeValue(
1247 new ASN1OctetString(valueString));
1248 matchingRuleSet.add(new AttributeValue(rawValue, normValue));
1249 }
1250 }
1251
1252
1253
1254 /**
1255 * Deregisters the provided approximate matching rule definition
1256 * with this schema.
1257 *
1258 * @param matchingRule The approximate matching rule to deregister
1259 * with this schema.
1260 */
1261 public void deregisterApproximateMatchingRule(
1262 ApproximateMatchingRule matchingRule)
1263 {
1264 synchronized (matchingRules)
1265 {
1266 String oid = matchingRule.getOID();
1267 approximateMatchingRules.remove(oid, matchingRule);
1268 matchingRules.remove(oid, matchingRule);
1269
1270 String name = matchingRule.getName();
1271 if (name != null)
1272 {
1273 name = toLowerCase(name);
1274 approximateMatchingRules.remove(name, matchingRule);
1275 matchingRules.remove(name, matchingRule);
1276 }
1277
1278 // We'll use an attribute value including the normalized value
1279 // rather than the attribute type because otherwise it would use
1280 // a very expensive matching rule (OID first component match)
1281 // that would kill performance.
1282 try
1283 {
1284 String valueString = matchingRule.toString();
1285 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1286 ByteString normValue =
1287 normalizationMatchingRule.normalizeValue(
1288 new ASN1OctetString(valueString));
1289 matchingRuleSet.remove(new AttributeValue(rawValue,
1290 normValue));
1291 }
1292 catch (Exception e)
1293 {
1294 String valueString = matchingRule.toString();
1295 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1296 ASN1OctetString normValue =
1297 new ASN1OctetString(toLowerCase(valueString));
1298 matchingRuleSet.remove(new AttributeValue(rawValue,
1299 normValue));
1300 }
1301 }
1302 }
1303
1304
1305
1306 /**
1307 * Retrieves the equality matching rule definitions for this schema,
1308 * as a mapping between the lowercase names and OIDs for the
1309 * matching rule and the matching rule itself. Each matching rule
1310 * may be associated with multiple keys (once for the OID and again
1311 * for each name). The contents of the returned mapping must not be
1312 * altered.
1313 *
1314 * @return The equality matching rule definitions for this schema.
1315 */
1316 public ConcurrentHashMap<String,EqualityMatchingRule>
1317 getEqualityMatchingRules()
1318 {
1319 return equalityMatchingRules;
1320 }
1321
1322
1323
1324 /**
1325 * Retrieves the equality matching rule definition with the
1326 * specified name or OID.
1327 *
1328 * @param lowerName The name or OID of the matching rule to
1329 * retrieve, formatted in all lowercase
1330 * characters.
1331 *
1332 * @return The requested matching rule, or <CODE>null</CODE> if no
1333 * equality matching rule is registered with the provided
1334 * name or OID.
1335 */
1336 public EqualityMatchingRule getEqualityMatchingRule(
1337 String lowerName)
1338 {
1339 return equalityMatchingRules.get(lowerName);
1340 }
1341
1342
1343
1344 /**
1345 * Registers the provided equality matching rule with this schema.
1346 *
1347 * @param matchingRule The equality matching rule to
1348 * register.
1349 * @param overwriteExisting Indicates whether to overwrite an
1350 * existing mapping if there are any
1351 * conflicts (i.e., another matching rule
1352 * with the same OID or name).
1353 *
1354 * @throws DirectoryException If a conflict is encountered and the
1355 * <CODE>overwriteExisting</CODE> flag
1356 * is set to <CODE>false</CODE>
1357 */
1358 public void registerEqualityMatchingRule(
1359 EqualityMatchingRule matchingRule,
1360 boolean overwriteExisting)
1361 throws DirectoryException
1362 {
1363 synchronized (matchingRules)
1364 {
1365 if (! overwriteExisting)
1366 {
1367 String oid = toLowerCase(matchingRule.getOID());
1368 if (matchingRules.containsKey(oid))
1369 {
1370 MatchingRule conflictingRule = matchingRules.get(oid);
1371
1372 Message message = ERR_SCHEMA_CONFLICTING_MR_OID.
1373 get(matchingRule.getNameOrOID(), oid,
1374 conflictingRule.getNameOrOID());
1375 throw new DirectoryException(
1376 ResultCode.CONSTRAINT_VIOLATION, message);
1377 }
1378
1379 String name = matchingRule.getName();
1380 if (name != null)
1381 {
1382 name = toLowerCase(name);
1383 if (matchingRules.containsKey(name))
1384 {
1385 MatchingRule conflictingRule = matchingRules.get(name);
1386
1387 Message message = ERR_SCHEMA_CONFLICTING_MR_NAME.
1388 get(matchingRule.getOID(), name,
1389 conflictingRule.getOID());
1390 throw new DirectoryException(
1391 ResultCode.CONSTRAINT_VIOLATION, message);
1392 }
1393 }
1394 }
1395
1396 String oid = toLowerCase(matchingRule.getOID());
1397 equalityMatchingRules.put(oid, matchingRule);
1398 matchingRules.put(oid, matchingRule);
1399
1400 String name = matchingRule.getName();
1401 if (name != null)
1402 {
1403 name = toLowerCase(name);
1404 equalityMatchingRules.put(name, matchingRule);
1405 matchingRules.put(name, matchingRule);
1406 }
1407
1408 // We'll use an attribute value including the normalized value
1409 // rather than the attribute type because otherwise it would use
1410 // a very expensive matching rule (OID first component match)
1411 // that would kill performance.
1412 String valueString = matchingRule.toString();
1413 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1414 ByteString normValue = normalizationMatchingRule.normalizeValue(
1415 new ASN1OctetString(valueString));
1416 matchingRuleSet.add(new AttributeValue(rawValue, normValue));
1417 }
1418 }
1419
1420
1421
1422 /**
1423 * Deregisters the provided equality matching rule definition with
1424 * this schema.
1425 *
1426 * @param matchingRule The equality matching rule to deregister
1427 * with this schema.
1428 */
1429 public void deregisterEqualityMatchingRule(
1430 EqualityMatchingRule matchingRule)
1431 {
1432 synchronized (matchingRules)
1433 {
1434 String oid = matchingRule.getOID();
1435 equalityMatchingRules.remove(oid, matchingRule);
1436 matchingRules.remove(oid, matchingRule);
1437
1438 String name = matchingRule.getName();
1439 if (name != null)
1440 {
1441 name = toLowerCase(name);
1442 equalityMatchingRules.remove(name, matchingRule);
1443 matchingRules.remove(name, matchingRule);
1444 }
1445
1446
1447 // We'll use an attribute value including the normalized value
1448 // rather than the attribute type because otherwise it would use
1449 // a very expensive matching rule (OID first component match)
1450 // that would kill performance.
1451 try
1452 {
1453 String valueString = matchingRule.toString();
1454 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1455 ByteString normValue =
1456 normalizationMatchingRule.normalizeValue(
1457 new ASN1OctetString(valueString));
1458 matchingRuleSet.remove(new AttributeValue(rawValue,
1459 normValue));
1460 }
1461 catch (Exception e)
1462 {
1463 String valueString = matchingRule.toString();
1464 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1465 ASN1OctetString normValue =
1466 new ASN1OctetString(toLowerCase(valueString));
1467 matchingRuleSet.remove(new AttributeValue(rawValue,
1468 normValue));
1469 }
1470 }
1471 }
1472
1473
1474
1475 /**
1476 * Retrieves the ordering matching rule definitions for this schema,
1477 * as a mapping between the lowercase names and OIDs for the
1478 * matching rule and the matching rule itself. Each matching rule
1479 * may be associated with multiple keys (once for the OID and again
1480 * for each name). The contents of the returned mapping must not be
1481 * altered.
1482 *
1483 * @return The ordering matching rule definitions for this schema.
1484 */
1485 public ConcurrentHashMap<String,OrderingMatchingRule>
1486 getOrderingMatchingRules()
1487 {
1488 return orderingMatchingRules;
1489 }
1490
1491
1492
1493 /**
1494 * Retrieves the ordering matching rule definition with the
1495 * specified name or OID.
1496 *
1497 * @param lowerName The name or OID of the matching rule to
1498 * retrieve, formatted in all lowercase
1499 * characters.
1500 *
1501 * @return The requested matching rule, or <CODE>null</CODE> if no
1502 * ordering matching rule is registered with the provided
1503 * name or OID.
1504 */
1505 public OrderingMatchingRule getOrderingMatchingRule(
1506 String lowerName)
1507 {
1508 return orderingMatchingRules.get(lowerName);
1509 }
1510
1511
1512
1513 /**
1514 * Registers the provided ordering matching rule with this schema.
1515 *
1516 * @param matchingRule The ordering matching rule to
1517 * register.
1518 * @param overwriteExisting Indicates whether to overwrite an
1519 * existing mapping if there are any
1520 * conflicts (i.e., another matching rule
1521 * with the same OID or name).
1522 *
1523 * @throws DirectoryException If a conflict is encountered and the
1524 * <CODE>overwriteExisting</CODE> flag
1525 * is set to <CODE>false</CODE>
1526 */
1527 public void registerOrderingMatchingRule(
1528 OrderingMatchingRule matchingRule,
1529 boolean overwriteExisting)
1530 throws DirectoryException
1531 {
1532 synchronized (matchingRules)
1533 {
1534 if (! overwriteExisting)
1535 {
1536 String oid = toLowerCase(matchingRule.getOID());
1537 if (matchingRules.containsKey(oid))
1538 {
1539 MatchingRule conflictingRule = matchingRules.get(oid);
1540
1541 Message message = ERR_SCHEMA_CONFLICTING_MR_OID.
1542 get(matchingRule.getNameOrOID(), oid,
1543 conflictingRule.getNameOrOID());
1544 throw new DirectoryException(
1545 ResultCode.CONSTRAINT_VIOLATION, message);
1546 }
1547
1548 String name = matchingRule.getName();
1549 if (name != null)
1550 {
1551 name = toLowerCase(name);
1552 if (matchingRules.containsKey(name))
1553 {
1554 MatchingRule conflictingRule = matchingRules.get(name);
1555
1556 Message message = ERR_SCHEMA_CONFLICTING_MR_NAME.
1557 get(matchingRule.getOID(), name,
1558 conflictingRule.getOID());
1559 throw new DirectoryException(
1560 ResultCode.CONSTRAINT_VIOLATION, message);
1561 }
1562 }
1563 }
1564
1565 String oid = toLowerCase(matchingRule.getOID());
1566 orderingMatchingRules.put(oid, matchingRule);
1567 matchingRules.put(oid, matchingRule);
1568
1569 String name = matchingRule.getName();
1570 if (name != null)
1571 {
1572 name = toLowerCase(name);
1573 orderingMatchingRules.put(name, matchingRule);
1574 matchingRules.put(name, matchingRule);
1575 }
1576
1577 // We'll use an attribute value including the normalized value
1578 // rather than the attribute type because otherwise it would use
1579 // a very expensive matching rule (OID first component match)
1580 // that would kill performance.
1581 String valueString = matchingRule.toString();
1582 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1583 ByteString normValue = normalizationMatchingRule.normalizeValue(
1584 new ASN1OctetString(valueString));
1585 matchingRuleSet.add(new AttributeValue(rawValue, normValue));
1586 }
1587 }
1588
1589
1590
1591 /**
1592 * Deregisters the provided ordering matching rule definition with
1593 * this schema.
1594 *
1595 * @param matchingRule The ordering matching rule to deregister
1596 * with this schema.
1597 */
1598 public void deregisterOrderingMatchingRule(
1599 OrderingMatchingRule matchingRule)
1600 {
1601 synchronized (matchingRules)
1602 {
1603 String oid = matchingRule.getOID();
1604 orderingMatchingRules.remove(oid, matchingRule);
1605 matchingRules.remove(oid, matchingRule);
1606
1607 String name = matchingRule.getName();
1608 if (name != null)
1609 {
1610 name = toLowerCase(name);
1611 orderingMatchingRules.remove(name, matchingRule);
1612 matchingRules.remove(name, matchingRule);
1613 }
1614
1615 // We'll use an attribute value including the normalized value
1616 // rather than the attribute type because otherwise it would use
1617 // a very expensive matching rule (OID first component match)
1618 // that would kill performance.
1619 try
1620 {
1621 String valueString = matchingRule.toString();
1622 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1623 ByteString normValue =
1624 normalizationMatchingRule.normalizeValue(
1625 new ASN1OctetString(valueString));
1626 matchingRuleSet.remove(new AttributeValue(rawValue,
1627 normValue));
1628 }
1629 catch (Exception e)
1630 {
1631 String valueString = matchingRule.toString();
1632 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1633 ASN1OctetString normValue =
1634 new ASN1OctetString(toLowerCase(valueString));
1635 matchingRuleSet.remove(new AttributeValue(rawValue,
1636 normValue));
1637 }
1638 }
1639 }
1640
1641
1642
1643 /**
1644 * Retrieves the substring matching rule definitions for this
1645 * schema, as a mapping between the lowercase names and OIDs for the
1646 * matching rule and the matching rule itself. Each matching rule
1647 * may be associated with multiple keys (once for the OID and again
1648 * for each name). The contents of the returned mapping must not be
1649 * altered.
1650 *
1651 * @return The substring matching rule definitions for this schema.
1652 */
1653 public ConcurrentHashMap<String,SubstringMatchingRule>
1654 getSubstringMatchingRules()
1655 {
1656 return substringMatchingRules;
1657 }
1658
1659
1660
1661 /**
1662 * Retrieves the substring matching rule definition with the
1663 * specified name or OID.
1664 *
1665 * @param lowerName The name or OID of the matching rule to
1666 * retrieve, formatted in all lowercase
1667 * characters.
1668 *
1669 * @return The requested matching rule, or <CODE>null</CODE> if no
1670 * substring matching rule is registered with the provided
1671 * name or OID.
1672 */
1673 public SubstringMatchingRule getSubstringMatchingRule(
1674 String lowerName)
1675 {
1676 return substringMatchingRules.get(lowerName);
1677 }
1678
1679
1680
1681 /**
1682 * Registers the provided substring matching rule with this schema.
1683 *
1684 * @param matchingRule The substring matching rule to
1685 * register.
1686 * @param overwriteExisting Indicates whether to overwrite an
1687 * existing mapping if there are any
1688 * conflicts (i.e., another matching rule
1689 * with the same OID or name).
1690 *
1691 * @throws DirectoryException If a conflict is encountered and the
1692 * <CODE>overwriteExisting</CODE> flag
1693 * is set to <CODE>false</CODE>
1694 */
1695 public void registerSubstringMatchingRule(
1696 SubstringMatchingRule matchingRule,
1697 boolean overwriteExisting)
1698 throws DirectoryException
1699 {
1700 synchronized (matchingRules)
1701 {
1702 if (! overwriteExisting)
1703 {
1704 String oid = toLowerCase(matchingRule.getOID());
1705 if (matchingRules.containsKey(oid))
1706 {
1707 MatchingRule conflictingRule = matchingRules.get(oid);
1708
1709 Message message = ERR_SCHEMA_CONFLICTING_MR_OID.
1710 get(matchingRule.getNameOrOID(), oid,
1711 conflictingRule.getNameOrOID());
1712 throw new DirectoryException(
1713 ResultCode.CONSTRAINT_VIOLATION, message);
1714 }
1715
1716 String name = matchingRule.getName();
1717 if (name != null)
1718 {
1719 name = toLowerCase(name);
1720 if (matchingRules.containsKey(name))
1721 {
1722 MatchingRule conflictingRule = matchingRules.get(name);
1723
1724 Message message = ERR_SCHEMA_CONFLICTING_MR_NAME.
1725 get(matchingRule.getOID(), name,
1726 conflictingRule.getOID());
1727 throw new DirectoryException(
1728 ResultCode.CONSTRAINT_VIOLATION, message);
1729 }
1730 }
1731 }
1732
1733 String oid = toLowerCase(matchingRule.getOID());
1734 substringMatchingRules.put(oid, matchingRule);
1735 matchingRules.put(oid, matchingRule);
1736
1737 String name = matchingRule.getName();
1738 if (name != null)
1739 {
1740 name = toLowerCase(name);
1741 substringMatchingRules.put(name, matchingRule);
1742 matchingRules.put(name, matchingRule);
1743 }
1744
1745 // We'll use an attribute value including the normalized value
1746 // rather than the attribute type because otherwise it would use
1747 // a very expensive matching rule (OID first component match)
1748 // that would kill performance.
1749 String valueString = matchingRule.toString();
1750 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1751 ByteString normValue = normalizationMatchingRule.normalizeValue(
1752 new ASN1OctetString(valueString));
1753 matchingRuleSet.add(new AttributeValue(rawValue, normValue));
1754 }
1755 }
1756
1757
1758
1759 /**
1760 * Deregisters the provided substring matching rule definition with
1761 * this schema.
1762 *
1763 * @param matchingRule The substring matching rule to deregister
1764 * with this schema.
1765 */
1766 public void deregisterSubstringMatchingRule(
1767 SubstringMatchingRule matchingRule)
1768 {
1769 synchronized (matchingRules)
1770 {
1771 String oid = matchingRule.getOID();
1772 substringMatchingRules.remove(oid, matchingRule);
1773 matchingRules.remove(oid, matchingRule);
1774
1775 String name = matchingRule.getName();
1776 if (name != null)
1777 {
1778 name = toLowerCase(name);
1779 substringMatchingRules.remove(name, matchingRule);
1780 matchingRules.remove(name, matchingRule);
1781 }
1782
1783 // We'll use an attribute value including the normalized value
1784 // rather than the attribute type because otherwise it would use
1785 // a very expensive matching rule (OID first component match)
1786 // that would kill performance.
1787 try
1788 {
1789 String valueString = matchingRule.toString();
1790 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1791 ByteString normValue =
1792 normalizationMatchingRule.normalizeValue(
1793 new ASN1OctetString(valueString));
1794 matchingRuleSet.remove(new AttributeValue(rawValue,
1795 normValue));
1796 }
1797 catch (Exception e)
1798 {
1799 String valueString = matchingRule.toString();
1800 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1801 ASN1OctetString normValue =
1802 new ASN1OctetString(toLowerCase(valueString));
1803 matchingRuleSet.remove(new AttributeValue(rawValue,
1804 normValue));
1805 }
1806 }
1807 }
1808
1809
1810
1811 /**
1812 * Retrieves the matching rule use definitions for this schema, as a
1813 * mapping between the matching rule for the matching rule use
1814 * definition and the matching rule use itself. Each matching rule
1815 * use should only be present once, since its only key is its
1816 * matching rule. The contents of the returned mapping must not be
1817 * altered.
1818 *
1819 * @return The matching rule use definitions for this schema.
1820 */
1821 public ConcurrentHashMap<MatchingRule,MatchingRuleUse>
1822 getMatchingRuleUses()
1823 {
1824 return matchingRuleUses;
1825 }
1826
1827
1828
1829 /**
1830 * Retrieves the set of defined matching rule uses for this schema.
1831 *
1832 * @return The set of defined matching rule uses for this schema.
1833 */
1834 public LinkedHashSet<AttributeValue> getMatchingRuleUseSet()
1835 {
1836 return matchingRuleUseSet;
1837 }
1838
1839
1840
1841 /**
1842 * Indicates whether this schema definition includes a matching rule
1843 * use for the provided matching rule.
1844 *
1845 * @param matchingRule The matching rule for which to make the
1846 * determination.
1847 *
1848 * @return {@code true} if this schema contains a matching rule use
1849 * for the provided matching rule, or {@code false} if not.
1850 */
1851 public boolean hasMatchingRuleUse(MatchingRule matchingRule)
1852 {
1853 return matchingRuleUses.containsKey(matchingRule);
1854 }
1855
1856
1857
1858 /**
1859 * Retrieves the matching rule use definition for the specified
1860 * matching rule.
1861 *
1862 * @param matchingRule The matching rule for which to retrieve the
1863 * matching rule use definition.
1864 *
1865 * @return The matching rule use definition, or <CODE>null</CODE>
1866 * if none exists for the specified matching rule.
1867 */
1868 public MatchingRuleUse getMatchingRuleUse(MatchingRule matchingRule)
1869 {
1870 return matchingRuleUses.get(matchingRule);
1871 }
1872
1873
1874
1875 /**
1876 * Registers the provided matching rule use definition with this
1877 * schema.
1878 *
1879 * @param matchingRuleUse The matching rule use definition to
1880 * register.
1881 * @param overwriteExisting Indicates whether to overwrite an
1882 * existing mapping if there are any
1883 * conflicts (i.e., another matching rule
1884 * use with the same matching rule).
1885 *
1886 * @throws DirectoryException If a conflict is encountered and the
1887 * <CODE>overwriteExisting</CODE> flag
1888 * is set to <CODE>false</CODE>
1889 */
1890 public void registerMatchingRuleUse(MatchingRuleUse matchingRuleUse,
1891 boolean overwriteExisting)
1892 throws DirectoryException
1893 {
1894 synchronized (matchingRuleUses)
1895 {
1896 MatchingRule matchingRule = matchingRuleUse.getMatchingRule();
1897
1898 if (! overwriteExisting)
1899 {
1900 if (matchingRuleUses.containsKey(matchingRule))
1901 {
1902 MatchingRuleUse conflictingUse =
1903 matchingRuleUses.get(matchingRule);
1904
1905 Message message = ERR_SCHEMA_CONFLICTING_MATCHING_RULE_USE.
1906 get(matchingRuleUse.getName(),
1907 matchingRule.getNameOrOID(),
1908 conflictingUse.getName());
1909 throw new DirectoryException(
1910 ResultCode.CONSTRAINT_VIOLATION, message);
1911 }
1912 }
1913
1914 matchingRuleUses.put(matchingRule, matchingRuleUse);
1915
1916 // We'll use an attribute value including the normalized value
1917 // rather than the attribute type because otherwise it would use
1918 // a very expensive matching rule (OID first component match)
1919 // that would kill performance.
1920 String valueString = matchingRuleUse.getDefinition();
1921 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1922 ByteString normValue = normalizationMatchingRule.normalizeValue(
1923 new ASN1OctetString(valueString));
1924 matchingRuleUseSet.add(new AttributeValue(rawValue, normValue));
1925 }
1926 }
1927
1928
1929
1930 /**
1931 * Deregisters the provided matching rule use definition with this
1932 * schema.
1933 *
1934 * @param matchingRuleUse The matching rule use to deregister with
1935 * this schema.
1936 */
1937 public void deregisterMatchingRuleUse(
1938 MatchingRuleUse matchingRuleUse)
1939 {
1940 synchronized (matchingRuleUses)
1941 {
1942 matchingRuleUses.remove(matchingRuleUse.getMatchingRule(),
1943 matchingRuleUse);
1944
1945 // We'll use an attribute value including the normalized value
1946 // rather than the attribute type because otherwise it would use
1947 // a very expensive matching rule (OID first component match)
1948 // that would kill performance.
1949 try
1950 {
1951 String valueString = matchingRuleUse.getDefinition();
1952 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1953 ByteString normValue =
1954 normalizationMatchingRule.normalizeValue(
1955 new ASN1OctetString(valueString));
1956 matchingRuleUseSet.remove(new AttributeValue(rawValue,
1957 normValue));
1958 }
1959 catch (Exception e)
1960 {
1961 String valueString = matchingRuleUse.getDefinition();
1962 ASN1OctetString rawValue = new ASN1OctetString(valueString);
1963 ASN1OctetString normValue =
1964 new ASN1OctetString(toLowerCase(valueString));
1965 matchingRuleUseSet.remove(new AttributeValue(rawValue,
1966 normValue));
1967 }
1968 }
1969 }
1970
1971
1972
1973 /**
1974 * Retrieves the DIT content rule definitions for this schema, as a
1975 * mapping between the objectclass for the rule and the DIT content
1976 * rule itself. Each DIT content rule should only be present once,
1977 * since its only key is its objectclass. The contents of the
1978 * returned mapping must not be altered.
1979 *
1980 * @return The DIT content rule definitions for this schema.
1981 */
1982 public ConcurrentHashMap<ObjectClass,DITContentRule>
1983 getDITContentRules()
1984 {
1985 return ditContentRules;
1986 }
1987
1988
1989
1990 /**
1991 * Retrieves the set of defined DIT content rules for this schema.
1992 *
1993 * @return The set of defined DIT content rules for this schema.
1994 */
1995 public LinkedHashSet<AttributeValue> getDITContentRuleSet()
1996 {
1997 return ditContentRuleSet;
1998 }
1999
2000
2001
2002 /**
2003 * Indicates whether this schema definition includes a DIT content
2004 * rule for the provided objectclass.
2005 *
2006 * @param objectClass The objectclass for which to make the
2007 * determination.
2008 *
2009 * @return {@code true} if this schema contains a DIT content rule
2010 * for the provided objectclass, or {@code false} if not.
2011 */
2012 public boolean hasDITContentRule(ObjectClass objectClass)
2013 {
2014 return ditContentRules.containsKey(objectClass);
2015 }
2016
2017
2018
2019 /**
2020 * Retrieves the DIT content rule definition for the specified
2021 * objectclass.
2022 *
2023 * @param objectClass The objectclass for the DIT content rule to
2024 * retrieve.
2025 *
2026 * @return The requested DIT content rule, or <CODE>null</CODE> if
2027 * no DIT content rule is registered with the provided
2028 * objectclass.
2029 */
2030 public DITContentRule getDITContentRule(ObjectClass objectClass)
2031 {
2032 return ditContentRules.get(objectClass);
2033 }
2034
2035
2036
2037 /**
2038 * Registers the provided DIT content rule definition with this
2039 * schema.
2040 *
2041 * @param ditContentRule The DIT content rule to register.
2042 * @param overwriteExisting Indicates whether to overwrite an
2043 * existing mapping if there are any
2044 * conflicts (i.e., another DIT content
2045 * rule with the same objectclass).
2046 *
2047 * @throws DirectoryException If a conflict is encountered and the
2048 * <CODE>overwriteExisting</CODE> flag
2049 * is set to <CODE>false</CODE>
2050 */
2051 public void registerDITContentRule(DITContentRule ditContentRule,
2052 boolean overwriteExisting)
2053 throws DirectoryException
2054 {
2055 synchronized (ditContentRules)
2056 {
2057 ObjectClass objectClass = ditContentRule.getStructuralClass();
2058
2059 if (! overwriteExisting)
2060 {
2061 if (ditContentRules.containsKey(objectClass))
2062 {
2063 DITContentRule conflictingRule =
2064 ditContentRules.get(objectClass);
2065
2066 Message message = ERR_SCHEMA_CONFLICTING_DIT_CONTENT_RULE.
2067 get(ditContentRule.getName(),
2068 objectClass.getNameOrOID(),
2069 conflictingRule.getName());
2070 throw new DirectoryException(
2071 ResultCode.CONSTRAINT_VIOLATION, message);
2072 }
2073 }
2074
2075 ditContentRules.put(objectClass, ditContentRule);
2076
2077 // We'll use an attribute value including the normalized value
2078 // rather than the attribute type because otherwise it would use
2079 // a very expensive matching rule (OID first component match)
2080 // that would kill performance.
2081 String valueString = ditContentRule.getDefinition();
2082 ASN1OctetString rawValue = new ASN1OctetString(valueString);
2083 ByteString normValue = normalizationMatchingRule.normalizeValue(
2084 new ASN1OctetString(valueString));
2085 ditContentRuleSet.add(new AttributeValue(rawValue, normValue));
2086 }
2087 }
2088
2089
2090
2091 /**
2092 * Deregisters the provided DIT content rule definition with this
2093 * schema.
2094 *
2095 * @param ditContentRule The DIT content rule to deregister with
2096 * this schema.
2097 */
2098 public void deregisterDITContentRule(DITContentRule ditContentRule)
2099 {
2100 synchronized (ditContentRules)
2101 {
2102 ditContentRules.remove(ditContentRule.getStructuralClass(),
2103 ditContentRule);
2104
2105 // We'll use an attribute value including the normalized value
2106 // rather than the attribute type because otherwise it would use
2107 // a very expensive matching rule (OID first component match)
2108 // that would kill performance.
2109 try
2110 {
2111 String valueString = ditContentRule.getDefinition();
2112 ASN1OctetString rawValue = new ASN1OctetString(valueString);
2113 ByteString normValue =
2114 normalizationMatchingRule.normalizeValue(
2115 new ASN1OctetString(valueString));
2116 ditContentRuleSet.remove(new AttributeValue(rawValue,
2117 normValue));
2118 }
2119 catch (Exception e)
2120 {
2121 String valueString = ditContentRule.getDefinition();
2122 ASN1OctetString rawValue = new ASN1OctetString(valueString);
2123 ASN1OctetString normValue =
2124 new ASN1OctetString(toLowerCase(valueString));
2125 ditContentRuleSet.remove(new AttributeValue(rawValue,
2126 normValue));
2127 }
2128 }
2129 }
2130
2131
2132
2133 /**
2134 * Retrieves the set of defined DIT structure rules for this schema.
2135 *
2136 * @return The set of defined DIT structure rules for this schema.
2137 */
2138 public LinkedHashSet<AttributeValue> getDITStructureRuleSet()
2139 {
2140 return ditStructureRuleSet;
2141 }
2142
2143
2144
2145 /**
2146 * Retrieves the DIT structure rule definitions for this schema, as
2147 * a mapping between the rule ID for the rule and the DIT structure
2148 * rule itself. Each DIT structure rule should only be present
2149 * once, since its only key is its rule ID. The contents of the
2150 * returned mapping must not be altered.
2151 *
2152 * @return The DIT structure rule definitions for this schema.
2153 */
2154 public ConcurrentHashMap<Integer,DITStructureRule>
2155 getDITStructureRulesByID()
2156 {
2157 return ditStructureRulesByID;
2158 }
2159
2160
2161
2162 /**
2163 * Retrieves the DIT structure rule definitions for this schema, as
2164 * a mapping between the name form for the rule and the DIT
2165 * structure rule itself. Each DIT structure rule should only be
2166 * present once, since its only key is its name form. The contents
2167 * of the returned mapping must not be altered.
2168 *
2169 * @return The DIT structure rule definitions for this schema.
2170 */
2171 public ConcurrentHashMap<NameForm,DITStructureRule>
2172 getDITStructureRulesByNameForm()
2173 {
2174 return ditStructureRulesByNameForm;
2175 }
2176
2177
2178
2179 /**
2180 * Indicates whether this schema definition includes a DIT structure
2181 * rule with the provided rule ID.
2182 *
2183 * @param ruleID The rule ID for which to make the determination.
2184 *
2185 * @return {@code true} if this schema contains a DIT structure
2186 * rule with the provided rule ID, or {@code false} if not.
2187 */
2188 public boolean hasDITStructureRule(int ruleID)
2189 {
2190 return ditStructureRulesByID.containsKey(ruleID);
2191 }
2192
2193
2194
2195 /**
2196 * Indicates whether this schema definition includes a DIT structure
2197 * rule for the provided name form.
2198 *
2199 * @param nameForm The name form for which to make the
2200 * determination.
2201 *
2202 * @return {@code true} if this schema contains a DIT structure
2203 * rule for the provided name form, or {@code false} if
2204 * not.
2205 */
2206 public boolean hasDITStructureRule(NameForm nameForm)
2207 {
2208 return ditStructureRulesByNameForm.containsKey(nameForm);
2209 }
2210
2211
2212
2213 /**
2214 * Retrieves the DIT structure rule definition with the provided
2215 * rule ID.
2216 *
2217 * @param ruleID The rule ID for the DIT structure rule to
2218 * retrieve.
2219 *
2220 * @return The requested DIT structure rule, or <CODE>null</CODE>
2221 * if no DIT structure rule is registered with the provided
2222 * rule ID.
2223 */
2224 public DITStructureRule getDITStructureRule(int ruleID)
2225 {
2226 return ditStructureRulesByID.get(ruleID);
2227 }
2228
2229
2230
2231 /**
2232 * Retrieves the DIT structure rule definition for the provided name
2233 * form.
2234 *
2235 * @param nameForm The name form for the DIT structure rule to
2236 * retrieve.
2237 *
2238 * @return The requested DIT structure rule, or <CODE>null</CODE>
2239 * if no DIT structure rule is registered with the provided
2240 * name form.
2241 */
2242 public DITStructureRule getDITStructureRule(NameForm nameForm)
2243 {
2244 return ditStructureRulesByNameForm.get(nameForm);
2245 }
2246
2247
2248
2249 /**
2250 * Registers the provided DIT structure rule definition with this
2251 * schema.
2252 *
2253 * @param ditStructureRule The DIT structure rule to register.
2254 * @param overwriteExisting Indicates whether to overwrite an
2255 * existing mapping if there are any
2256 * conflicts (i.e., another DIT structure
2257 * rule with the same name form).
2258 *
2259 * @throws DirectoryException If a conflict is encountered and the
2260 * <CODE>overwriteExisting</CODE> flag
2261 * is set to <CODE>false</CODE>
2262 */
2263 public void registerDITStructureRule(
2264 DITStructureRule ditStructureRule,
2265 boolean overwriteExisting)
2266 throws DirectoryException
2267 {
2268 synchronized (ditStructureRulesByNameForm)
2269 {
2270 NameForm nameForm = ditStructureRule.getNameForm();
2271 int ruleID = ditStructureRule.getRuleID();
2272
2273 if (! overwriteExisting)
2274 {
2275 if (ditStructureRulesByNameForm.containsKey(nameForm))
2276 {
2277 DITStructureRule conflictingRule =
2278 ditStructureRulesByNameForm.get(nameForm);
2279
2280 Message message =
2281 ERR_SCHEMA_CONFLICTING_DIT_STRUCTURE_RULE_NAME_FORM.
2282 get(ditStructureRule.getNameOrRuleID(),
2283 nameForm.getNameOrOID(),
2284 conflictingRule.getNameOrRuleID());
2285 throw new DirectoryException(
2286 ResultCode.CONSTRAINT_VIOLATION, message);
2287 }
2288
2289 if (ditStructureRulesByID.containsKey(ruleID))
2290 {
2291 DITStructureRule conflictingRule =
2292 ditStructureRulesByID.get(ruleID);
2293
2294 Message message =
2295 ERR_SCHEMA_CONFLICTING_DIT_STRUCTURE_RULE_ID.
2296 get(ditStructureRule.getNameOrRuleID(), ruleID,
2297 conflictingRule.getNameOrRuleID());
2298 throw new DirectoryException(
2299 ResultCode.CONSTRAINT_VIOLATION, message);
2300 }
2301 }
2302
2303 ditStructureRulesByNameForm.put(nameForm, ditStructureRule);
2304 ditStructureRulesByID.put(ruleID, ditStructureRule);
2305
2306 // We'll use an attribute value including the normalized value
2307 // rather than the attribute type because otherwise it would use
2308 // a very expensive matching rule (OID first component match)
2309 // that would kill performance.
2310 String valueString = ditStructureRule.getDefinition();
2311 ASN1OctetString rawValue = new ASN1OctetString(valueString);
2312 ByteString normValue = normalizationMatchingRule.normalizeValue(
2313 new ASN1OctetString(valueString));
2314 ditStructureRuleSet.add(new AttributeValue(rawValue,
2315 normValue));
2316 }
2317 }
2318
2319
2320
2321 /**
2322 * Deregisters the provided DIT structure rule definition with this
2323 * schema.
2324 *
2325 * @param ditStructureRule The DIT structure rule to deregister
2326 * with this schema.
2327 */
2328 public void deregisterDITStructureRule(
2329 DITStructureRule ditStructureRule)
2330 {
2331 synchronized (ditStructureRulesByNameForm)
2332 {
2333 ditStructureRulesByNameForm.remove(
2334 ditStructureRule.getNameForm(), ditStructureRule);
2335 ditStructureRulesByID.remove(ditStructureRule.getRuleID(),
2336 ditStructureRule);
2337
2338 // We'll use an attribute value including the normalized value
2339 // rather than the attribute type because otherwise it would use
2340 // a very expensive matching rule (OID first component match)
2341 // that would kill performance.
2342 try
2343 {
2344 String valueString = ditStructureRule.getDefinition();
2345 ASN1OctetString rawValue = new ASN1OctetString(valueString);
2346 ByteString normValue =
2347 normalizationMatchingRule.normalizeValue(
2348 new ASN1OctetString(valueString));
2349 ditStructureRuleSet.remove(new AttributeValue(rawValue,
2350 normValue));
2351 }
2352 catch (Exception e)
2353 {
2354 String valueString = ditStructureRule.getDefinition();
2355 ASN1OctetString rawValue = new ASN1OctetString(valueString);
2356 ASN1OctetString normValue =
2357 new ASN1OctetString(toLowerCase(valueString));
2358 ditStructureRuleSet.remove(new AttributeValue(rawValue,
2359 normValue));
2360 }
2361 }
2362 }
2363
2364
2365
2366 /**
2367 * Retrieves the set of defined name forms for this schema.
2368 *
2369 * @return The set of defined name forms for this schema.
2370 */
2371 public LinkedHashSet<AttributeValue> getNameFormSet()
2372 {
2373 return nameFormSet;
2374 }
2375
2376
2377
2378 /**
2379 * Retrieves the name form definitions for this schema, as a mapping
2380 * between the objectclass for the name form and the name form
2381 * itself. Each name form should only be present once, since its
2382 * only key is its objectclass. The contents of the returned
2383 * mapping must not be altered.
2384 *
2385 * @return The name form definitions for this schema.
2386 */
2387 public ConcurrentHashMap<ObjectClass,NameForm>
2388 getNameFormsByObjectClass()
2389 {
2390 return nameFormsByOC;
2391 }
2392
2393
2394
2395 /**
2396 * Retrieves the name form definitions for this schema, as a mapping
2397 * between the names/OID for the name form and the name form itself.
2398 * Each name form may be present multiple times with different names
2399 * and its OID. The contents of the returned mapping must not be
2400 * altered.
2401 *
2402 * @return The name form definitions for this schema.
2403 */
2404 public ConcurrentHashMap<String,NameForm> getNameFormsByNameOrOID()
2405 {
2406 return nameFormsByName;
2407 }
2408
2409
2410
2411 /**
2412 * Indicates whether this schema definition includes a name form for
2413 * the specified objectclass.
2414 *
2415 * @param objectClass The objectclass for which to make the
2416 * determination.
2417 *
2418 * @return {@code true} if this schema contains a name form for the
2419 * provided objectclass, or {@code false} if not.
2420 */
2421 public boolean hasNameForm(ObjectClass objectClass)
2422 {
2423 return nameFormsByOC.containsKey(objectClass);
2424 }
2425
2426
2427
2428 /**
2429 * Indicates whether this schema definition includes a name form
2430 * with the specified name or OID.
2431 *
2432 * @param lowerName The name or OID for which to make the
2433 * determination, formatted in all lowercase
2434 * characters.
2435 *
2436 * @return {@code true} if this schema contains a name form with
2437 * the provided name or OID, or {@code false} if not.
2438 */
2439 public boolean hasNameForm(String lowerName)
2440 {
2441 return nameFormsByName.containsKey(lowerName);
2442 }
2443
2444
2445
2446 /**
2447 * Retrieves the name form definition for the specified objectclass.
2448 *
2449 * @param objectClass The objectclass for the name form to
2450 * retrieve.
2451 *
2452 * @return The requested name form, or <CODE>null</CODE> if no name
2453 * form is registered with the provided objectClass.
2454 */
2455 public NameForm getNameForm(ObjectClass objectClass)
2456 {
2457 return nameFormsByOC.get(objectClass);
2458 }
2459
2460
2461
2462 /**
2463 * Retrieves the name form definition with the provided name or OID.
2464 *
2465 * @param lowerName The name or OID of the name form to retrieve,
2466 * formatted in all lowercase characters.
2467 *
2468 * @return The requested name form, or <CODE>null</CODE> if no name
2469 * form is registered with the provided name or OID.
2470 */
2471 public NameForm getNameForm(String lowerName)
2472 {
2473 return nameFormsByName.get(lowerName);
2474 }
2475
2476
2477
2478 /**
2479 * Registers the provided name form definition with this schema.
2480 *
2481 * @param nameForm The name form definition to register.
2482 * @param overwriteExisting Indicates whether to overwrite an
2483 * existing mapping if there are any
2484 * conflicts (i.e., another name form
2485 * with the same objectclass).
2486 *
2487 * @throws DirectoryException If a conflict is encountered and the
2488 * <CODE>overwriteExisting</CODE> flag
2489 * is set to <CODE>false</CODE>
2490 */
2491 public void registerNameForm(NameForm nameForm,
2492 boolean overwriteExisting)
2493 throws DirectoryException
2494 {
2495 synchronized (nameFormsByOC)
2496 {
2497 ObjectClass objectClass = nameForm.getStructuralClass();
2498
2499 if (! overwriteExisting)
2500 {
2501 if (nameFormsByOC.containsKey(objectClass))
2502 {
2503 NameForm conflictingNameForm =
2504 nameFormsByOC.get(objectClass);
2505
2506 Message message = ERR_SCHEMA_CONFLICTING_NAME_FORM_OC.
2507 get(nameForm.getNameOrOID(), objectClass.getNameOrOID(),
2508 conflictingNameForm.getNameOrOID());
2509 throw new DirectoryException(
2510 ResultCode.CONSTRAINT_VIOLATION, message);
2511 }
2512
2513 String oid = toLowerCase(nameForm.getOID());
2514 if (nameFormsByName.containsKey(oid))
2515 {
2516 NameForm conflictingNameForm = nameFormsByName.get(oid);
2517
2518 Message message = ERR_SCHEMA_CONFLICTING_NAME_FORM_OID.
2519 get(nameForm.getNameOrOID(), oid,
2520 conflictingNameForm.getNameOrOID());
2521 throw new DirectoryException(
2522 ResultCode.CONSTRAINT_VIOLATION, message);
2523 }
2524
2525 for (String name : nameForm.getNames().keySet())
2526 {
2527 if (nameFormsByName.containsKey(name))
2528 {
2529 NameForm conflictingNameForm = nameFormsByName.get(name);
2530
2531 Message message = ERR_SCHEMA_CONFLICTING_NAME_FORM_NAME.
2532 get(nameForm.getNameOrOID(), oid,
2533 conflictingNameForm.getNameOrOID());
2534 throw new DirectoryException(
2535 ResultCode.CONSTRAINT_VIOLATION, message);
2536 }
2537 }
2538 }
2539
2540 nameFormsByOC.put(objectClass, nameForm);
2541 nameFormsByName.put(toLowerCase(nameForm.getOID()), nameForm);
2542
2543 for (String name : nameForm.getNames().keySet())
2544 {
2545 nameFormsByName.put(name, nameForm);
2546 }
2547
2548 // We'll use an attribute value including the normalized value
2549 // rather than the attribute type because otherwise it would use
2550 // a very expensive matching rule (OID first component match)
2551 // that would kill performance.
2552 String valueString = nameForm.getDefinition();
2553 ASN1OctetString rawValue = new ASN1OctetString(valueString);
2554 ByteString normValue = normalizationMatchingRule.normalizeValue(
2555 new ASN1OctetString(valueString));
2556 nameFormSet.add(new AttributeValue(rawValue, normValue));
2557 }
2558 }
2559
2560
2561
2562 /**
2563 * Deregisters the provided name form definition with this schema.
2564 *
2565 * @param nameForm The name form definition to deregister.
2566 */
2567 public void deregisterNameForm(NameForm nameForm)
2568 {
2569 synchronized (nameFormsByOC)
2570 {
2571 nameFormsByOC.remove(nameForm.getStructuralClass(), nameForm);
2572 nameFormsByName.remove(toLowerCase(nameForm.getOID()),
2573 nameForm);
2574
2575 for (String name : nameForm.getNames().keySet())
2576 {
2577 nameFormsByName.remove(name, nameForm);
2578 }
2579
2580 // We'll use an attribute value including the normalized value
2581 // rather than the attribute type because otherwise it would use
2582 // a very expensive matching rule (OID first component match)
2583 // that would kill performance.
2584 try
2585 {
2586 String valueString = nameForm.getDefinition();
2587 ASN1OctetString rawValue = new ASN1OctetString(valueString);
2588 ByteString normValue =
2589 normalizationMatchingRule.normalizeValue(
2590 new ASN1OctetString(valueString));
2591 nameFormSet.remove(new AttributeValue(rawValue, normValue));
2592 }
2593 catch (Exception e)
2594 {
2595 String valueString = nameForm.getDefinition();
2596 ASN1OctetString rawValue = new ASN1OctetString(valueString);
2597 ASN1OctetString normValue =
2598 new ASN1OctetString(toLowerCase(valueString));
2599 nameFormSet.remove(new AttributeValue(rawValue, normValue));
2600 }
2601 }
2602 }
2603
2604
2605
2606 /**
2607 * Retrieves the modification timestamp for the file in the schema
2608 * configuration directory with the oldest last modified time.
2609 *
2610 * @return The modification timestamp for the file in the schema
2611 * configuration directory with the oldest last modified
2612 * time.
2613 */
2614 public long getOldestModificationTime()
2615 {
2616 return oldestModificationTime;
2617 }
2618
2619
2620
2621 /**
2622 * Sets the modification timestamp for the oldest file in the schema
2623 * configuration directory.
2624 *
2625 * @param oldestModificationTime The modification timestamp for
2626 * the oldest file in the schema
2627 * configuration directory.
2628 */
2629 public void setOldestModificationTime(long oldestModificationTime)
2630 {
2631 this.oldestModificationTime = oldestModificationTime;
2632 }
2633
2634
2635
2636 /**
2637 * Retrieves the modification timestamp for the file in the schema
2638 * configuration directory with the youngest last modified time.
2639 *
2640 * @return The modification timestamp for the file in the schema
2641 * configuration directory with the youngest last modified
2642 * time.
2643 */
2644 public long getYoungestModificationTime()
2645 {
2646 return youngestModificationTime;
2647 }
2648
2649
2650
2651 /**
2652 * Sets the modification timestamp for the youngest file in the
2653 * schema configuration directory.
2654 *
2655 * @param youngestModificationTime The modification timestamp for
2656 * the youngest file in the schema
2657 * configuration directory.
2658 */
2659 public void setYoungestModificationTime(
2660 long youngestModificationTime)
2661 {
2662 this.youngestModificationTime = youngestModificationTime;
2663 }
2664
2665
2666
2667 /**
2668 * Recursively rebuilds all schema elements that are dependent upon
2669 * the provided element. This must be invoked whenever an existing
2670 * schema element is modified in order to ensure that any elements
2671 * that depend on it should also be recreated to reflect the change.
2672 * <BR><BR>
2673 * The following conditions create dependencies between schema
2674 * elements:
2675 * <UL>
2676 * <LI>If an attribute type references a superior attribute type,
2677 * then it is dependent upon that superior attribute
2678 * type.</LI>
2679 * <LI>If an objectclass requires or allows an attribute type,
2680 * then it is dependent upon that attribute type.</LI>
2681 * <LI>If a name form requires or allows an attribute type in the
2682 * RDN, then it is dependent upon that attribute type.</LI>
2683 * <LI>If a DIT content rule requires, allows, or forbids the use
2684 * of an attribute type, then it is dependent upon that
2685 * attribute type.</LI>
2686 * <LI>If a matching rule use references an attribute type, then
2687 * it is dependent upon that attribute type.</LI>
2688 * <LI>If an objectclass references a superior objectclass, then
2689 * it is dependent upon that superior objectclass.</LI>
2690 * <LI>If a name form references a structural objectclass, then it
2691 * is dependent upon that objectclass.</LI>
2692 * <LI>If a DIT content rule references a structural or auxiliary
2693 * objectclass, then it is dependent upon that
2694 * objectclass.</LI>
2695 * <LI>If a DIT structure rule references a name form, then it is
2696 * dependent upon that name form.</LI>
2697 * <LI>If a DIT structure rule references a superior DIT structure
2698 * rule, then it is dependent upon that superior DIT structure
2699 * rule.</LI>
2700 * </UL>
2701 *
2702 * @param element The element for which to recursively rebuild all
2703 * dependent elements.
2704 *
2705 * @throws DirectoryException If a problem occurs while rebuilding
2706 * any of the schema elements.
2707 */
2708 public void rebuildDependentElements(SchemaFileElement element)
2709 throws DirectoryException
2710 {
2711 try
2712 {
2713 rebuildDependentElements(element, 0);
2714 }
2715 catch (DirectoryException de)
2716 {
2717 // If we got an error as a result of a circular reference, then
2718 // we want to make sure that the schema element we call out is
2719 // the one that is at the root of the problem.
2720 if (de.getMessageObject().getDescriptor().equals(
2721 ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE))
2722 {
2723 Message message = ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE.
2724 get(element.getDefinition());
2725 throw new DirectoryException(de.getResultCode(), message,
2726 de);
2727 }
2728
2729
2730 // It wasn't a circular reference error, so just re-throw the
2731 // exception.
2732 throw de;
2733 }
2734 }
2735
2736
2737
2738 /**
2739 * Recursively rebuilds all schema elements that are dependent upon
2740 * the provided element, increasing the depth for each level of
2741 * recursion to protect against errors due to circular references.
2742 *
2743 * @param element The element for which to recursively rebuild all
2744 * dependent elements.
2745 * @param depth The current recursion depth.
2746 *
2747 * @throws DirectoryException If a problem occurs while rebuilding
2748 * any of the schema elements.
2749 */
2750 private void rebuildDependentElements(SchemaFileElement element,
2751 int depth)
2752 throws DirectoryException
2753 {
2754 if (depth > 20)
2755 {
2756 // FIXME -- Is this an appropriate maximum depth for detecting
2757 // circular references?
2758 Message message = ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE.get(
2759 element.getDefinition());
2760 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
2761 message);
2762 }
2763
2764
2765 // Figure out what type of element we're dealing with and make the
2766 // appropriate determinations for that element.
2767 if (element instanceof AttributeType)
2768 {
2769 AttributeType t = (AttributeType) element;
2770
2771 for (AttributeType at : attributeTypes.values())
2772 {
2773 if ((at.getSuperiorType() != null) &&
2774 at.getSuperiorType().equals(t))
2775 {
2776 AttributeType newAT = at.recreateFromDefinition();
2777 deregisterAttributeType(at);
2778 registerAttributeType(newAT, true);
2779 rebuildDependentElements(at, depth+1);
2780 }
2781 }
2782
2783 for (ObjectClass oc : objectClasses.values())
2784 {
2785 if (oc.getRequiredAttributes().contains(t) ||
2786 oc.getOptionalAttributes().contains(t))
2787 {
2788 ObjectClass newOC = oc.recreateFromDefinition();
2789 deregisterObjectClass(oc);
2790 registerObjectClass(newOC, true);
2791 rebuildDependentElements(oc, depth+1);
2792 }
2793 }
2794
2795 for (NameForm nf : nameFormsByOC.values())
2796 {
2797 if (nf.getRequiredAttributes().contains(t) ||
2798 nf.getOptionalAttributes().contains(t))
2799 {
2800 NameForm newNF = nf.recreateFromDefinition();
2801 deregisterNameForm(nf);
2802 registerNameForm(newNF, true);
2803 rebuildDependentElements(nf, depth+1);
2804 }
2805 }
2806
2807 for (DITContentRule dcr : ditContentRules.values())
2808 {
2809 if (dcr.getRequiredAttributes().contains(t) ||
2810 dcr.getOptionalAttributes().contains(t) ||
2811 dcr.getProhibitedAttributes().contains(t))
2812 {
2813 DITContentRule newDCR = dcr.recreateFromDefinition();
2814 deregisterDITContentRule(dcr);
2815 registerDITContentRule(newDCR, true);
2816 rebuildDependentElements(dcr, depth+1);
2817 }
2818 }
2819
2820 for (MatchingRuleUse mru : matchingRuleUses.values())
2821 {
2822 if (mru.getAttributes().contains(t))
2823 {
2824 MatchingRuleUse newMRU = mru.recreateFromDefinition();
2825 deregisterMatchingRuleUse(mru);
2826 registerMatchingRuleUse(newMRU, true);
2827 rebuildDependentElements(mru, depth+1);
2828 }
2829 }
2830 }
2831 else if (element instanceof ObjectClass)
2832 {
2833 ObjectClass c = (ObjectClass) element;
2834
2835 for (ObjectClass oc : objectClasses.values())
2836 {
2837 if ((oc.getSuperiorClass() != null) &&
2838 oc.getSuperiorClass().equals(c))
2839 {
2840 ObjectClass newOC = oc.recreateFromDefinition();
2841 deregisterObjectClass(oc);
2842 registerObjectClass(newOC, true);
2843 rebuildDependentElements(oc, depth+1);
2844 }
2845 }
2846
2847 NameForm nf = nameFormsByOC.get(c);
2848 if (nf != null)
2849 {
2850 NameForm newNF = nf.recreateFromDefinition();
2851 deregisterNameForm(nf);
2852 registerNameForm(newNF, true);
2853 rebuildDependentElements(nf, depth+1);
2854 }
2855
2856 for (DITContentRule dcr : ditContentRules.values())
2857 {
2858 if (dcr.getStructuralClass().equals(c) ||
2859 dcr.getAuxiliaryClasses().contains(c))
2860 {
2861 DITContentRule newDCR = dcr.recreateFromDefinition();
2862 deregisterDITContentRule(dcr);
2863 registerDITContentRule(newDCR, true);
2864 rebuildDependentElements(dcr, depth+1);
2865 }
2866 }
2867 }
2868 else if (element instanceof NameForm)
2869 {
2870 NameForm n = (NameForm) element;
2871 DITStructureRule dsr = ditStructureRulesByNameForm.get(n);
2872 if (dsr != null)
2873 {
2874 DITStructureRule newDSR = dsr.recreateFromDefinition();
2875 deregisterDITStructureRule(dsr);
2876 registerDITStructureRule(newDSR, true);
2877 rebuildDependentElements(dsr, depth+1);
2878 }
2879 }
2880 else if (element instanceof DITStructureRule)
2881 {
2882 DITStructureRule d = (DITStructureRule) element;
2883 for (DITStructureRule dsr : ditStructureRulesByID.values())
2884 {
2885 if (dsr.getSuperiorRules().contains(d))
2886 {
2887 DITStructureRule newDSR = dsr.recreateFromDefinition();
2888 deregisterDITStructureRule(dsr);
2889 registerDITStructureRule(newDSR, true);
2890 rebuildDependentElements(dsr, depth+1);
2891 }
2892 }
2893 }
2894 }
2895
2896
2897
2898 /**
2899 * Creates a new <CODE>Schema</CODE> object that is a duplicate of
2900 * this one. It elements may be added and removed from the
2901 * duplicate without impacting this version.
2902 *
2903 * @return A new <CODE>Schema</CODE> object that is a duplicate of
2904 * this one.
2905 */
2906 public Schema duplicate()
2907 {
2908 Schema dupSchema = new Schema();
2909
2910 dupSchema.attributeTypes.putAll(attributeTypes);
2911 dupSchema.subordinateTypes.putAll(subordinateTypes);
2912 dupSchema.objectClasses.putAll(objectClasses);
2913 dupSchema.syntaxes.putAll(syntaxes);
2914 dupSchema.matchingRules.putAll(matchingRules);
2915 dupSchema.approximateMatchingRules.putAll(
2916 approximateMatchingRules);
2917 dupSchema.equalityMatchingRules.putAll(equalityMatchingRules);
2918 dupSchema.orderingMatchingRules.putAll(orderingMatchingRules);
2919 dupSchema.substringMatchingRules.putAll(substringMatchingRules);
2920 dupSchema.matchingRuleUses.putAll(matchingRuleUses);
2921 dupSchema.ditContentRules.putAll(ditContentRules);
2922 dupSchema.ditStructureRulesByID.putAll(ditStructureRulesByID);
2923 dupSchema.ditStructureRulesByNameForm.putAll(
2924 ditStructureRulesByNameForm);
2925 dupSchema.nameFormsByOC.putAll(nameFormsByOC);
2926 dupSchema.nameFormsByName.putAll(nameFormsByName);
2927 dupSchema.syntaxSet.addAll(syntaxSet);
2928 dupSchema.attributeTypeSet.addAll(attributeTypeSet);
2929 dupSchema.ditContentRuleSet.addAll(ditContentRuleSet);
2930 dupSchema.ditStructureRuleSet.addAll(ditStructureRuleSet);
2931 dupSchema.matchingRuleSet.addAll(matchingRuleSet);
2932 dupSchema.matchingRuleUseSet.addAll(matchingRuleUseSet);
2933 dupSchema.nameFormSet.addAll(nameFormSet);
2934 dupSchema.objectClassSet.addAll(objectClassSet);
2935 dupSchema.oldestModificationTime = oldestModificationTime;
2936 dupSchema.youngestModificationTime = youngestModificationTime;
2937 if (extraAttributes != null)
2938 {
2939 dupSchema.extraAttributes =
2940 new HashMap<String, Attribute>(extraAttributes);
2941 }
2942
2943 return dupSchema;
2944 }
2945
2946
2947 /**
2948 * Get the extraAttributes stored in this schema.
2949 *
2950 * @return The extraAttributes stored in this schema.
2951 */
2952 public Map<String, Attribute> getExtraAttributes()
2953 {
2954 return extraAttributes;
2955 }
2956
2957
2958 /**
2959 * Add a new extra Attribute for this schema.
2960 *
2961 * @param name The identifier of the extra Attribute.
2962 *
2963 * @param attr The extra attribute that must be added to
2964 * this Schema.
2965 */
2966 public void addExtraAttribute(String name, Attribute attr)
2967 {
2968 extraAttributes.put(name, attr);
2969 }
2970
2971
2972 /**
2973 * Writes a single file containing all schema element definitions,
2974 * which can be used on startup to determine whether the schema
2975 * files were edited with the server offline.
2976 */
2977 public static void writeConcatenatedSchema()
2978 {
2979 String concatFilePath = null;
2980 try
2981 {
2982 LinkedHashSet<String> attributeTypes =
2983 new LinkedHashSet<String>();
2984 LinkedHashSet<String> objectClasses =
2985 new LinkedHashSet<String>();
2986 LinkedHashSet<String> nameForms = new LinkedHashSet<String>();
2987 LinkedHashSet<String> ditContentRules =
2988 new LinkedHashSet<String>();
2989 LinkedHashSet<String> ditStructureRules =
2990 new LinkedHashSet<String>();
2991 LinkedHashSet<String> matchingRuleUses =
2992 new LinkedHashSet<String>();
2993 genConcatenatedSchema(attributeTypes, objectClasses, nameForms,
2994 ditContentRules, ditStructureRules,
2995 matchingRuleUses);
2996
2997
2998 File configFile = new File(DirectoryServer.getConfigFile());
2999 File configDirectory = configFile.getParentFile();
3000 File upgradeDirectory = new File(configDirectory, "upgrade");
3001 upgradeDirectory.mkdir();
3002 File concatFile = new File(upgradeDirectory,
3003 SCHEMA_CONCAT_FILE_NAME);
3004 concatFilePath = concatFile.getAbsolutePath();
3005
3006 File tempFile = new File(concatFilePath + ".tmp");
3007 BufferedWriter writer =
3008 new BufferedWriter(new FileWriter(tempFile, false));
3009 writer.write("dn: " + DirectoryServer.getSchemaDN().toString());
3010 writer.newLine();
3011 writer.write("objectClass: top");
3012 writer.newLine();
3013 writer.write("objectClass: ldapSubentry");
3014 writer.newLine();
3015 writer.write("objectClass: subschema");
3016 writer.newLine();
3017
3018 for (String line : attributeTypes)
3019 {
3020 writer.write(ATTR_ATTRIBUTE_TYPES);
3021 writer.write(": ");
3022 writer.write(line);
3023 writer.newLine();
3024 }
3025
3026 for (String line : objectClasses)
3027 {
3028 writer.write(ATTR_OBJECTCLASSES);
3029 writer.write(": ");
3030 writer.write(line);
3031 writer.newLine();
3032 }
3033
3034 for (String line : nameForms)
3035 {
3036 writer.write(ATTR_NAME_FORMS);
3037 writer.write(": ");
3038 writer.write(line);
3039 writer.newLine();
3040 }
3041
3042 for (String line : ditContentRules)
3043 {
3044 writer.write(ATTR_DIT_CONTENT_RULES);
3045 writer.write(": ");
3046 writer.write(line);
3047 writer.newLine();
3048 }
3049
3050 for (String line : ditStructureRules)
3051 {
3052 writer.write(ATTR_DIT_STRUCTURE_RULES);
3053 writer.write(": ");
3054 writer.write(line);
3055 writer.newLine();
3056 }
3057
3058 for (String line : matchingRuleUses)
3059 {
3060 writer.write(ATTR_MATCHING_RULE_USE);
3061 writer.write(": ");
3062 writer.write(line);
3063 writer.newLine();
3064 }
3065
3066 writer.close();
3067
3068 if (concatFile.exists())
3069 {
3070 concatFile.delete();
3071 }
3072 tempFile.renameTo(concatFile);
3073 }
3074 catch (Exception e)
3075 {
3076 if (debugEnabled())
3077 {
3078 TRACER.debugCaught(DebugLogLevel.ERROR, e);
3079 }
3080
3081 // This is definitely not ideal, but it's not the end of the
3082 // world. The worst that should happen is that the schema
3083 // changes could potentially be sent to the other servers again
3084 // when this server is restarted, which shouldn't hurt anything.
3085 // Still, we should log a warning message.
3086 logError(ERR_SCHEMA_CANNOT_WRITE_CONCAT_SCHEMA_FILE.get(
3087 String.valueOf(concatFilePath), getExceptionMessage(e)));
3088 }
3089 }
3090
3091
3092
3093 /**
3094 * Reads the files contained in the schema directory and generates a
3095 * concatenated view of their contents in the provided sets.
3096 *
3097 * @param attributeTypes The set into which to place the
3098 * attribute types read from the schema
3099 * files.
3100 * @param objectClasses The set into which to place the object
3101 * classes read from the schema files.
3102 * @param nameForms The set into which to place the name
3103 * forms read from the schema files.
3104 * @param ditContentRules The set into which to place the DIT
3105 * content rules read from the schema
3106 * files.
3107 * @param ditStructureRules The set into which to place the DIT
3108 * structure rules read from the schema
3109 * files.
3110 * @param matchingRuleUses The set into which to place the
3111 * matching rule uses read from the
3112 * schema files.
3113 *
3114 * @throws IOException If a problem occurs while reading the
3115 * schema file elements.
3116 */
3117 public static void genConcatenatedSchema(
3118 LinkedHashSet<String> attributeTypes,
3119 LinkedHashSet<String> objectClasses,
3120 LinkedHashSet<String> nameForms,
3121 LinkedHashSet<String> ditContentRules,
3122 LinkedHashSet<String> ditStructureRules,
3123 LinkedHashSet<String> matchingRuleUses)
3124 throws IOException
3125 {
3126 // Get a sorted list of the files in the schema directory.
3127 String schemaDirectory =
3128 SchemaConfigManager.getSchemaDirectoryPath();
3129 TreeSet<String> schemaFileNames = new TreeSet<String>();
3130 for (File f : new File(schemaDirectory).listFiles())
3131 {
3132 if (f.isFile())
3133 {
3134 schemaFileNames.add(f.getName());
3135 }
3136 }
3137
3138
3139 // Open each of the files in order and read the elements that they
3140 // contain, appending them to the appropriate lists.
3141 for (String name : schemaFileNames)
3142 {
3143 // Read the contents of the file into a list with one schema
3144 // element per list element.
3145 LinkedList<StringBuilder> lines =
3146 new LinkedList<StringBuilder>();
3147 BufferedReader reader =
3148 new BufferedReader(new FileReader(
3149 new File(schemaDirectory, name)));
3150
3151 while (true)
3152 {
3153 String line = reader.readLine();
3154 if (line == null)
3155 {
3156 break;
3157 }
3158 else if (line.startsWith("#") || (line.length() == 0))
3159 {
3160 continue;
3161 }
3162 else if (line.startsWith(" "))
3163 {
3164 lines.getLast().append(line.substring(1));
3165 }
3166 else
3167 {
3168 lines.add(new StringBuilder(line));
3169 }
3170 }
3171
3172 reader.close();
3173
3174
3175 // Iterate through each line in the list. Find the colon and
3176 // get the attribute name at the beginning. If it's someting
3177 // that we don't recognize, then skip it. Otherwise, add the
3178 // X-SCHEMA-FILE extension and add it to the appropriate schema
3179 // element list.
3180 for (StringBuilder buffer : lines)
3181 {
3182 // Get the line and add the X-SCHEMA-FILE extension to the end
3183 // of it. All of them should end with " )" but some might
3184 // have the parenthesis crammed up against the last character
3185 // so deal with that as well.
3186 String line = buffer.toString().trim();
3187 if (line.endsWith(" )"))
3188 {
3189 line = line.substring(0, line.length()-1) +
3190 SCHEMA_PROPERTY_FILENAME + " '" + name + "' )";
3191 }
3192 else if (line.endsWith(")"))
3193 {
3194 line = line.substring(0, line.length()-1) + " " +
3195 SCHEMA_PROPERTY_FILENAME + " '" + name + "' )";
3196 }
3197 else
3198 {
3199 continue;
3200 }
3201
3202 String value;
3203 String lowerLine = toLowerCase(line);
3204 if (lowerLine.startsWith(ATTR_ATTRIBUTE_TYPES_LC))
3205 {
3206 value = line.substring(
3207 ATTR_ATTRIBUTE_TYPES.length()+1).trim();
3208 attributeTypes.add(value);
3209 }
3210 else if (lowerLine.startsWith(ATTR_OBJECTCLASSES_LC))
3211 {
3212 value = line.substring(
3213 ATTR_OBJECTCLASSES.length()+1).trim();
3214 objectClasses.add(value);
3215 }
3216 else if (lowerLine.startsWith(ATTR_NAME_FORMS_LC))
3217 {
3218 value = line.substring(ATTR_NAME_FORMS.length()+1).trim();
3219 nameForms.add(value);
3220 }
3221 else if (lowerLine.startsWith(ATTR_DIT_CONTENT_RULES_LC))
3222 {
3223 value = line.substring(
3224 ATTR_DIT_CONTENT_RULES.length()+1).trim();
3225 ditContentRules.add(value);
3226 }
3227 else if (lowerLine.startsWith(ATTR_DIT_STRUCTURE_RULES_LC))
3228 {
3229 value = line.substring(
3230 ATTR_DIT_STRUCTURE_RULES.length()+1).trim();
3231 ditStructureRules.add(value);
3232 }
3233 else if (lowerLine.startsWith(ATTR_MATCHING_RULE_USE_LC))
3234 {
3235 value = line.substring(
3236 ATTR_MATCHING_RULE_USE.length()+1).trim();
3237 matchingRuleUses.add(value);
3238 }
3239 }
3240 }
3241 }
3242
3243
3244
3245 /**
3246 * Reads data from the specified concatenated schema file into the
3247 * provided sets.
3248 *
3249 * @param concatSchemaFile The path to the concatenated schema
3250 * file to be read.
3251 * @param attributeTypes The set into which to place the
3252 * attribute types read from the
3253 * concatenated schema file.
3254 * @param objectClasses The set into which to place the object
3255 * classes read from the concatenated
3256 * schema file.
3257 * @param nameForms The set into which to place the name
3258 * forms read from the concatenated
3259 * schema file.
3260 * @param ditContentRules The set into which to place the DIT
3261 * content rules read from the
3262 * concatenated schema file.
3263 * @param ditStructureRules The set into which to place the DIT
3264 * structure rules read from the
3265 * concatenated schema file.
3266 * @param matchingRuleUses The set into which to place the
3267 * matching rule uses read from the
3268 * concatenated schema file.
3269 *
3270 * @throws IOException If a problem occurs while reading the
3271 * schema file elements.
3272 */
3273 public static void readConcatenatedSchema(String concatSchemaFile,
3274 LinkedHashSet<String> attributeTypes,
3275 LinkedHashSet<String> objectClasses,
3276 LinkedHashSet<String> nameForms,
3277 LinkedHashSet<String> ditContentRules,
3278 LinkedHashSet<String> ditStructureRules,
3279 LinkedHashSet<String> matchingRuleUses)
3280 throws IOException
3281 {
3282 BufferedReader reader =
3283 new BufferedReader(new FileReader(concatSchemaFile));
3284 while (true)
3285 {
3286 String line = reader.readLine();
3287 if (line == null)
3288 {
3289 break;
3290 }
3291
3292 String value;
3293 String lowerLine = toLowerCase(line);
3294 if (lowerLine.startsWith(ATTR_ATTRIBUTE_TYPES_LC))
3295 {
3296 value =
3297 line.substring(ATTR_ATTRIBUTE_TYPES.length()+1).trim();
3298 attributeTypes.add(value);
3299 }
3300 else if (lowerLine.startsWith(ATTR_OBJECTCLASSES_LC))
3301 {
3302 value = line.substring(ATTR_OBJECTCLASSES.length()+1).trim();
3303 objectClasses.add(value);
3304 }
3305 else if (lowerLine.startsWith(ATTR_NAME_FORMS_LC))
3306 {
3307 value = line.substring(ATTR_NAME_FORMS.length()+1).trim();
3308 nameForms.add(value);
3309 }
3310 else if (lowerLine.startsWith(ATTR_DIT_CONTENT_RULES_LC))
3311 {
3312 value = line.substring(
3313 ATTR_DIT_CONTENT_RULES.length()+1).trim();
3314 ditContentRules.add(value);
3315 }
3316 else if (lowerLine.startsWith(ATTR_DIT_STRUCTURE_RULES_LC))
3317 {
3318 value = line.substring(
3319 ATTR_DIT_STRUCTURE_RULES.length()+1).trim();
3320 ditStructureRules.add(value);
3321 }
3322 else if (lowerLine.startsWith(ATTR_MATCHING_RULE_USE_LC))
3323 {
3324 value = line.substring(
3325 ATTR_MATCHING_RULE_USE.length()+1).trim();
3326 matchingRuleUses.add(value);
3327 }
3328 }
3329
3330 reader.close();
3331 }
3332
3333
3334
3335 /**
3336 * Compares the provided sets of schema element definitions and
3337 * writes any differences found into the given list of
3338 * modifications.
3339 *
3340 * @param oldElements The set of elements of the specified type
3341 * read from the previous concatenated schema
3342 * files.
3343 * @param newElements The set of elements of the specified type
3344 * read from the server's current schema.
3345 * @param elementType The attribute type associated with the
3346 * schema element being compared.
3347 * @param mods The list of modifications into which any
3348 * identified differences should be written.
3349 */
3350 public static void compareConcatenatedSchema(
3351 LinkedHashSet<String> oldElements,
3352 LinkedHashSet<String> newElements,
3353 AttributeType elementType,
3354 LinkedList<Modification> mods)
3355 {
3356 AttributeType attributeTypesType =
3357 DirectoryServer.getAttributeType(ATTR_ATTRIBUTE_TYPES_LC,
3358 true);
3359
3360 LinkedHashSet<AttributeValue> values =
3361 new LinkedHashSet<AttributeValue>();
3362 for (String s : oldElements)
3363 {
3364 if (! newElements.contains(s))
3365 {
3366 values.add(new AttributeValue(attributeTypesType, s));
3367 }
3368 }
3369
3370 if (! values.isEmpty())
3371 {
3372 mods.add(new Modification(ModificationType.DELETE,
3373 new Attribute(elementType,
3374 elementType.getNameOrOID(),
3375 values)));
3376 }
3377
3378
3379 values.clear();
3380 for (String s : newElements)
3381 {
3382 if (! oldElements.contains(s))
3383 {
3384 values.add(new AttributeValue(attributeTypesType, s));
3385 }
3386 }
3387
3388 if (! values.isEmpty())
3389 {
3390 mods.add(new Modification(ModificationType.ADD,
3391 new Attribute(elementType,
3392 elementType.getNameOrOID(),
3393 values)));
3394 }
3395 }
3396
3397
3398
3399 /**
3400 * Destroys the structures maintained by the schema so that they are
3401 * no longer usable. This should only be called at the end of the
3402 * server shutdown process, and it can help detect inappropriate
3403 * cached references.
3404 */
3405 @org.opends.server.types.PublicAPI(
3406 stability=org.opends.server.types.StabilityLevel.PRIVATE,
3407 mayInstantiate=false,
3408 mayExtend=false,
3409 mayInvoke=true)
3410 public synchronized void destroy()
3411 {
3412 if (approximateMatchingRules != null)
3413 {
3414 approximateMatchingRules.clear();
3415 approximateMatchingRules = null;
3416 }
3417
3418 if (attributeTypes != null)
3419 {
3420 attributeTypes.clear();
3421 attributeTypes = null;
3422 }
3423
3424 if (attributeTypeSet != null)
3425 {
3426 attributeTypeSet.clear();
3427 attributeTypeSet = null;
3428 }
3429
3430 if (ditContentRules != null)
3431 {
3432 ditContentRules.clear();
3433 ditContentRules = null;
3434 }
3435
3436 if (ditContentRuleSet != null)
3437 {
3438 ditContentRuleSet.clear();
3439 ditContentRuleSet = null;
3440 }
3441
3442 if (ditStructureRulesByID != null)
3443 {
3444 ditStructureRulesByID.clear();
3445 ditStructureRulesByID = null;
3446 }
3447
3448 if (ditStructureRulesByNameForm != null)
3449 {
3450 ditStructureRulesByNameForm.clear();
3451 ditStructureRulesByNameForm = null;
3452 }
3453
3454 if (ditStructureRuleSet != null)
3455 {
3456 ditStructureRuleSet.clear();
3457 ditStructureRuleSet = null;
3458 }
3459
3460 if (equalityMatchingRules != null)
3461 {
3462 equalityMatchingRules.clear();
3463 equalityMatchingRules = null;
3464 }
3465
3466 if (matchingRules != null)
3467 {
3468 matchingRules.clear();
3469 matchingRules = null;
3470 }
3471
3472 if (matchingRuleSet != null)
3473 {
3474 matchingRuleSet.clear();
3475 matchingRuleSet = null;
3476 }
3477
3478 if (matchingRuleUses != null)
3479 {
3480 matchingRuleUses.clear();
3481 matchingRuleUses = null;
3482 }
3483
3484 if (matchingRuleUseSet != null)
3485 {
3486 matchingRuleUseSet.clear();
3487 matchingRuleUseSet = null;
3488 }
3489
3490 if (nameFormsByName != null)
3491 {
3492 nameFormsByName.clear();
3493 nameFormsByName = null;
3494 }
3495
3496 if (nameFormsByOC != null)
3497 {
3498 nameFormsByOC.clear();
3499 nameFormsByOC = null;
3500 }
3501
3502 if (nameFormSet != null)
3503 {
3504 nameFormSet.clear();
3505 nameFormSet = null;
3506 }
3507
3508 if (objectClasses != null)
3509 {
3510 objectClasses.clear();
3511 objectClasses = null;
3512 }
3513
3514 if (objectClassSet != null)
3515 {
3516 objectClassSet.clear();
3517 objectClassSet = null;
3518 }
3519
3520 if (orderingMatchingRules != null)
3521 {
3522 orderingMatchingRules.clear();
3523 orderingMatchingRules = null;
3524 }
3525
3526 if (subordinateTypes != null)
3527 {
3528 subordinateTypes.clear();
3529 subordinateTypes = null;
3530 }
3531
3532 if (substringMatchingRules != null)
3533 {
3534 substringMatchingRules.clear();
3535 substringMatchingRules = null;
3536 }
3537
3538 if (extraAttributes != null)
3539 {
3540 extraAttributes.clear();
3541 extraAttributes = null;
3542 }
3543
3544 if (syntaxes != null)
3545 {
3546 syntaxes.clear();
3547 syntaxes = null;
3548 }
3549
3550 if (syntaxSet != null)
3551 {
3552 syntaxSet.clear();
3553 syntaxSet = null;
3554 }
3555 }
3556 }
3557