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 import org.opends.messages.Message; 029 import org.opends.messages.MessageBuilder; 030 031 032 import java.io.BufferedWriter; 033 import java.io.IOException; 034 import java.util.ArrayList; 035 import java.util.Collection; 036 import java.util.HashMap; 037 import java.util.HashSet; 038 import java.util.Iterator; 039 import java.util.LinkedHashMap; 040 import java.util.LinkedHashSet; 041 import java.util.LinkedList; 042 import java.util.List; 043 import java.util.Map; 044 import java.util.Set; 045 import java.util.concurrent.locks.Lock; 046 047 import org.opends.server.api.AttributeValueDecoder; 048 import org.opends.server.api.CompressedSchema; 049 import org.opends.server.api.ProtocolElement; 050 import org.opends.server.api.plugin.PluginResult; 051 import org.opends.server.core.DirectoryServer; 052 import org.opends.server.core.PluginConfigManager; 053 import org.opends.server.protocols.asn1.ASN1Element; 054 import org.opends.server.protocols.asn1.ASN1OctetString; 055 import org.opends.server.util.LDIFException; 056 057 import static org.opends.server.config.ConfigConstants.*; 058 import static org.opends.server.loggers.debug.DebugLogger.*; 059 import org.opends.server.loggers.debug.DebugTracer; 060 import static org.opends.server.loggers.ErrorLogger.*; 061 import static org.opends.messages.CoreMessages.*; 062 import static org.opends.messages.UtilityMessages.*; 063 import static org.opends.server.util.LDIFWriter.*; 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 for a Directory Server entry. 071 * It includes a DN and a set of attributes. 072 * <BR><BR> 073 * The entry also contains a volatile attachment object, which should 074 * be used to associate the entry with a special type of object that 075 * is based on its contents. For example, if the entry holds access 076 * control information, then the attachment might be an object that 077 * contains a representation of that access control definition in a 078 * more useful form. This is only useful if the entry is to be 079 * cached, since the attachment may be accessed if the entry is 080 * retrieved from the cache, but if the entry is retrieved from the 081 * backend repository it cannot be guaranteed to contain any 082 * attachment (and in most cases will not). This attachment is 083 * volatile in that it is not always guaranteed to be present, it may 084 * be removed or overwritten at any time, and it will be invalidated 085 * and removed if the entry is altered in any way. 086 */ 087 @org.opends.server.types.PublicAPI( 088 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED, 089 mayInstantiate=true, 090 mayExtend=false, 091 mayInvoke=true) 092 public class Entry 093 implements ProtocolElement 094 { 095 /** 096 * The tracer object for the debug logger. 097 */ 098 private static final DebugTracer TRACER = getTracer(); 099 100 // Indicates whether virtual attribute processing has been performed 101 // for this entry. 102 private boolean virtualAttributeProcessingPerformed; 103 104 // The set of operational attributes for this entry. 105 private Map<AttributeType,List<Attribute>> operationalAttributes; 106 107 // The set of user attributes for this entry. 108 private Map<AttributeType,List<Attribute>> userAttributes; 109 110 // The set of suppressed real attributes for this entry. 111 private Map<AttributeType,List<Attribute>> suppressedAttributes; 112 113 // The set of objectclasses for this entry. 114 private Map<ObjectClass,String> objectClasses; 115 116 // The DN for this entry. 117 private DN dn; 118 119 // A generic attachment that may be used to associate this entry 120 // with some other object. 121 private transient Object attachment; 122 123 // The schema used to govern this entry. 124 private Schema schema; 125 126 127 128 /** 129 * Creates a new entry with the provided information. 130 * 131 * @param dn The distinguished name for this 132 * entry. 133 * @param objectClasses The set of objectclasses for this 134 * entry as a mapping between the 135 * objectclass and the name to use to 136 * reference it. 137 * @param userAttributes The set of user attributes for 138 * this entry as a mapping between 139 * the attribute type and the list of 140 * attributes with that type. 141 * @param operationalAttributes The set of operational attributes 142 * for this entry as a mapping 143 * between the attribute type and the 144 * list of attributes with that type. 145 */ 146 public Entry(DN dn, Map<ObjectClass,String> objectClasses, 147 Map<AttributeType,List<Attribute>> userAttributes, 148 Map<AttributeType,List<Attribute>> 149 operationalAttributes) 150 { 151 attachment = null; 152 schema = DirectoryServer.getSchema(); 153 virtualAttributeProcessingPerformed = false; 154 155 156 suppressedAttributes = 157 new LinkedHashMap<AttributeType,List<Attribute>>(); 158 159 160 if (dn == null) 161 { 162 this.dn = DN.nullDN(); 163 } 164 else 165 { 166 this.dn = dn; 167 } 168 169 if (objectClasses == null) 170 { 171 this.objectClasses = new HashMap<ObjectClass,String>(); 172 } 173 else 174 { 175 this.objectClasses = objectClasses; 176 } 177 178 if (userAttributes == null) 179 { 180 this.userAttributes = 181 new HashMap<AttributeType,List<Attribute>>(); 182 } 183 else 184 { 185 this.userAttributes = userAttributes; 186 } 187 188 if (operationalAttributes == null) 189 { 190 this.operationalAttributes = 191 new HashMap<AttributeType,List<Attribute>>(); 192 } 193 else 194 { 195 this.operationalAttributes = operationalAttributes; 196 } 197 } 198 199 200 201 /** 202 * Retrieves the distinguished name for this entry. 203 * 204 * @return The distinguished name for this entry. 205 */ 206 public DN getDN() 207 { 208 return dn; 209 } 210 211 212 213 /** 214 * Specifies the distinguished name for this entry. 215 * 216 * @param dn The distinguished name for this entry. 217 */ 218 public void setDN(DN dn) 219 { 220 if (dn == null) 221 { 222 this.dn = DN.nullDN(); 223 } 224 else 225 { 226 this.dn = dn; 227 } 228 229 attachment = null; 230 } 231 232 233 234 /** 235 * Retrieves the set of objectclasses defined for this entry. The 236 * caller should be allowed to modify the contents of this list, but 237 * if it does then it should also invalidate the attachment. 238 * 239 * @return The set of objectclasses defined for this entry. 240 */ 241 public Map<ObjectClass,String> getObjectClasses() 242 { 243 return objectClasses; 244 } 245 246 247 248 /** 249 * Indicates whether this entry has the specified objectclass. 250 * 251 * @param objectClass The objectclass for which to make the 252 * determination. 253 * 254 * @return <CODE>true</CODE> if this entry has the specified 255 * objectclass, or <CODE>false</CODE> if not. 256 */ 257 public boolean hasObjectClass(ObjectClass objectClass) 258 { 259 return objectClasses.containsKey(objectClass); 260 } 261 262 263 264 /** 265 * Retrieves the structural objectclass for this entry. 266 * 267 * @return The structural objectclass for this entry, or 268 * <CODE>null</CODE> if there is none for some reason. If 269 * there are multiple structural classes in the entry, then 270 * the first will be returned. 271 */ 272 public ObjectClass getStructuralObjectClass() 273 { 274 ObjectClass structuralClass = null; 275 276 for (ObjectClass oc : objectClasses.keySet()) 277 { 278 if (oc.getObjectClassType() == ObjectClassType.STRUCTURAL) 279 { 280 if (structuralClass == null) 281 { 282 structuralClass = oc; 283 } 284 else 285 { 286 if (oc.isDescendantOf(structuralClass)) 287 { 288 structuralClass = oc; 289 } 290 } 291 } 292 } 293 294 return structuralClass; 295 } 296 297 298 299 /** 300 * Specifies the set of objectclasses for this entry. 301 * 302 * @param objectClassNames The values containing the names or OIDs 303 * of the objectClasses for this entry. 304 * 305 * @throws DirectoryException If a problem occurs while attempting 306 * to set the objectclasses for this 307 * entry. 308 */ 309 public void setObjectClasses( 310 Collection<AttributeValue> objectClassNames) 311 throws DirectoryException 312 { 313 attachment = null; 314 315 // Iterate through all the provided objectclass names and make 316 // sure that they are names of valid objectclasses. 317 LinkedHashMap<ObjectClass,String> ocMap = 318 new LinkedHashMap<ObjectClass,String>(); 319 for (AttributeValue v : objectClassNames) 320 { 321 String name = v.getStringValue(); 322 323 String lowerName; 324 try 325 { 326 lowerName = v.getNormalizedStringValue(); 327 } 328 catch (Exception e) 329 { 330 if (debugEnabled()) 331 { 332 TRACER.debugCaught(DebugLogLevel.ERROR, e); 333 } 334 335 lowerName = toLowerCase(v.getStringValue()); 336 } 337 338 ObjectClass oc = DirectoryServer.getObjectClass(lowerName); 339 if (oc == null) 340 { 341 Message message = 342 ERR_ENTRY_ADD_UNKNOWN_OC.get(name, String.valueOf(dn)); 343 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, 344 message); 345 } 346 347 ocMap.put(oc, name); 348 } 349 350 351 // If we've gotten here, then everything is fine so put the new 352 // set of objectclasses. 353 objectClasses = ocMap; 354 } 355 356 357 358 /** 359 * Adds the objectClass with the given name to this entry. 360 * 361 * @param objectClassName The value containing the name or OID of 362 * the objectClass to add to this entry. 363 * 364 * @throws DirectoryException If a problem occurs while attempting 365 * to add the objectclass to this 366 * entry. 367 */ 368 public void addObjectClass(AttributeValue objectClassName) 369 throws DirectoryException 370 { 371 attachment = null; 372 373 String name = objectClassName.getStringValue(); 374 375 String lowerName; 376 try 377 { 378 lowerName = objectClassName.getNormalizedStringValue(); 379 } 380 catch (Exception e) 381 { 382 if (debugEnabled()) 383 { 384 TRACER.debugCaught(DebugLogLevel.ERROR, e); 385 } 386 387 lowerName = toLowerCase(name); 388 } 389 390 ObjectClass oc = DirectoryServer.getObjectClass(lowerName, true); 391 if (objectClasses.containsKey(oc)) 392 { 393 Message message = 394 ERR_ENTRY_ADD_DUPLICATE_OC.get(name, String.valueOf(dn)); 395 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, 396 message); 397 } 398 399 objectClasses.put(oc, name); 400 } 401 402 403 404 /** 405 * Adds the provided objectClass to this entry. 406 * 407 * @param oc The objectClass to add to this entry. 408 * 409 * @throws DirectoryException If a problem occurs while attempting 410 * to add the objectclass to this 411 * entry. 412 */ 413 public void addObjectClass(ObjectClass oc) 414 throws DirectoryException 415 { 416 attachment = null; 417 418 if (objectClasses.containsKey(oc)) 419 { 420 Message message = ERR_ENTRY_ADD_DUPLICATE_OC.get( 421 oc.getNameOrOID(), String.valueOf(dn)); 422 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, 423 message); 424 } 425 426 objectClasses.put(oc, oc.getNameOrOID()); 427 } 428 429 430 431 /** 432 * Adds the objectclasses corresponding to the provided set of names 433 * to this entry. 434 * 435 * @param objectClassNames The values containing the names or OIDs 436 * of the objectClasses to add to this 437 * entry. 438 * 439 * @throws DirectoryException If a problem occurs while attempting 440 * to add the set of objectclasses to 441 * this entry. 442 */ 443 public void addObjectClasses( 444 Collection<AttributeValue> objectClassNames) 445 throws DirectoryException 446 { 447 attachment = null; 448 449 450 // Iterate through all the provided objectclass names and make 451 // sure that they are names of valid objectclasses not already 452 // assigned to the entry. 453 LinkedHashMap<ObjectClass,String> tmpOCMap = 454 new LinkedHashMap<ObjectClass,String>(); 455 for (AttributeValue v : objectClassNames) 456 { 457 String name = v.getStringValue(); 458 459 String lowerName; 460 try 461 { 462 lowerName = v.getNormalizedStringValue(); 463 } 464 catch (Exception e) 465 { 466 if (debugEnabled()) 467 { 468 TRACER.debugCaught(DebugLogLevel.ERROR, e); 469 } 470 471 lowerName = toLowerCase(v.getStringValue()); 472 } 473 474 ObjectClass oc = DirectoryServer.getObjectClass(lowerName); 475 if (oc == null) 476 { 477 Message message = 478 ERR_ENTRY_ADD_UNKNOWN_OC.get(name, String.valueOf(dn)); 479 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, 480 message); 481 } 482 483 if (objectClasses.containsKey(oc)) 484 { 485 Message message = 486 ERR_ENTRY_ADD_DUPLICATE_OC.get(name, String.valueOf(dn)); 487 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, 488 message); 489 } 490 491 if (oc.isObsolete()) 492 { 493 Message message = 494 ERR_ENTRY_ADD_OBSOLETE_OC.get(name, String.valueOf(dn)); 495 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, 496 message); 497 } 498 499 tmpOCMap.put(oc, name); 500 } 501 502 503 // If we've gotten here, then everything is OK, so add the new 504 // classes. 505 for (ObjectClass oc : tmpOCMap.keySet()) 506 { 507 String name = tmpOCMap.get(oc); 508 509 objectClasses.put(oc, name); 510 } 511 } 512 513 514 515 /** 516 * Retrieves the entire set of attributes for this entry. This will 517 * include both user and operational attributes. The caller must 518 * not modify the contents of this list. Also note that this method 519 * is less efficient than calling either (or both) 520 * <CODE>getUserAttributes</CODE> or 521 * <CODE>getOperationalAttributes</CODE>, so it should only be used 522 * when calls to those methods are not appropriate. 523 * 524 * @return The entire set of attributes for this entry. 525 */ 526 public List<Attribute> getAttributes() 527 { 528 ArrayList<Attribute> attributes = new ArrayList<Attribute>(); 529 530 for (List<Attribute> list : userAttributes.values()) 531 { 532 for (Attribute a : list) 533 { 534 attributes.add(a); 535 } 536 } 537 538 for (List<Attribute> list : operationalAttributes.values()) 539 { 540 for (Attribute a : list) 541 { 542 attributes.add(a); 543 } 544 } 545 546 return attributes; 547 } 548 549 550 551 /** 552 * Retrieves the entire set of user (i.e., non-operational) 553 * attributes for this entry. The caller should be allowed to 554 * modify the contents of this list, but if it does then it should 555 * also invalidate the attachment. 556 * 557 * @return The entire set of user attributes for this entry. 558 */ 559 public Map<AttributeType,List<Attribute>> getUserAttributes() 560 { 561 return userAttributes; 562 } 563 564 565 566 /** 567 * Retrieves the entire set of operational attributes for this 568 * entry. The caller should be allowed to modify the contents of 569 * this list, but if it does then it should also invalidate the 570 * attachment. 571 * 572 * @return The entire set of operational attributes for this entry. 573 */ 574 public Map<AttributeType,List<Attribute>> getOperationalAttributes() 575 { 576 return operationalAttributes; 577 } 578 579 580 581 /** 582 * Retrieves an attribute holding the objectclass information for 583 * this entry. The returned attribute must not be altered. 584 * 585 * @return An attribute holding the objectclass information for 586 * this entry, or <CODE>null</CODE> if it does not have any 587 * objectclass information. 588 */ 589 public Attribute getObjectClassAttribute() 590 { 591 if ((objectClasses == null) || objectClasses.isEmpty()) 592 { 593 return null; 594 } 595 596 AttributeType ocType = 597 DirectoryServer.getObjectClassAttributeType(); 598 599 LinkedHashSet<AttributeValue> ocValues = 600 new LinkedHashSet<AttributeValue>(objectClasses.size()); 601 for (String s : objectClasses.values()) 602 { 603 ocValues.add(new AttributeValue(ocType, 604 new ASN1OctetString(s))); 605 } 606 607 return new Attribute(ocType, ATTR_OBJECTCLASS, ocValues); 608 } 609 610 611 /** 612 * Indicates whether this entry contains the specified attribute. 613 * Any subordinate attribute of the specified attribute will also 614 * be used in the determination. 615 * 616 * 617 * @param attributeType The attribute type for which to 618 * make the determination. 619 * 620 * @return <CODE>true</CODE> if this entry contains the specified 621 * attribute, or <CODE>false</CODE> if not. 622 */ 623 public boolean hasAttribute(AttributeType attributeType) 624 { 625 return hasAttribute(attributeType, true); 626 } 627 628 629 /** 630 * Indicates whether this entry contains the specified attribute. 631 * 632 * @param attributeType The attribute type for which to 633 * make the determination. 634 * @param includeSubordinates Whether to include any subordinate 635 * attributes of the attribute type 636 * being retrieved. 637 * 638 * @return <CODE>true</CODE> if this entry contains the specified 639 * attribute, or <CODE>false</CODE> if not. 640 */ 641 public boolean hasAttribute(AttributeType attributeType, 642 boolean includeSubordinates) 643 { 644 if (userAttributes.containsKey(attributeType) || 645 operationalAttributes.containsKey(attributeType)) 646 { 647 return true; 648 } 649 650 if (includeSubordinates && 651 attributeType.mayHaveSubordinateTypes()) 652 { 653 for (AttributeType at : schema.getSubTypes(attributeType)) 654 { 655 if (userAttributes.containsKey(at) || 656 operationalAttributes.containsKey(at)) 657 { 658 return true; 659 } 660 } 661 } 662 663 return (attributeType.isObjectClassType() && 664 (! objectClasses.isEmpty())); 665 } 666 667 668 /** 669 * Indicates whether this entry contains the specified attribute 670 * with all of the options in the provided set. Any subordinate 671 * attribute of the specified attribute will also be used in 672 * the determination. 673 * 674 * @param attributeType The attribute type for which to 675 * make the determination. 676 * @param attributeOptions The set of options to use in the 677 * determination. 678 * 679 * @return <CODE>true</CODE> if this entry contains the specified 680 * attribute, or <CODE>false</CODE> if not. 681 */ 682 public boolean hasAttribute(AttributeType attributeType, 683 Set<String> attributeOptions) 684 { 685 return hasAttribute(attributeType, true, attributeOptions); 686 } 687 688 689 /** 690 * Indicates whether this entry contains the specified attribute 691 * with all of the options in the provided set. 692 * 693 * @param attributeType The attribute type for which to 694 * make the determination. 695 * @param includeSubordinates Whether to include any subordinate 696 * attributes of the attribute type 697 * being retrieved. 698 * @param attributeOptions The set of options to use in the 699 * determination. 700 * 701 * @return <CODE>true</CODE> if this entry contains the specified 702 * attribute, or <CODE>false</CODE> if not. 703 */ 704 public boolean hasAttribute(AttributeType attributeType, 705 boolean includeSubordinates, 706 Set<String> attributeOptions) 707 { 708 List<Attribute> attributes; 709 if (includeSubordinates && 710 attributeType.mayHaveSubordinateTypes()) 711 { 712 attributes = new LinkedList<Attribute>(); 713 List<Attribute> attrs = userAttributes.get(attributeType); 714 if (attrs != null) 715 { 716 attributes.addAll(attrs); 717 } 718 719 attrs = operationalAttributes.get(attributeType); 720 if (attrs != null) 721 { 722 attributes.addAll(attrs); 723 } 724 725 for (AttributeType at : schema.getSubTypes(attributeType)) 726 { 727 attrs = userAttributes.get(at); 728 if (attrs != null) 729 { 730 attributes.addAll(attrs); 731 } 732 733 attrs = operationalAttributes.get(at); 734 if (attrs != null) 735 { 736 attributes.addAll(attrs); 737 } 738 } 739 } 740 else 741 { 742 attributes = userAttributes.get(attributeType); 743 if (attributes == null) 744 { 745 attributes = operationalAttributes.get(attributeType); 746 if (attributes == null) 747 { 748 if (attributeType.isObjectClassType() && 749 (! objectClasses.isEmpty())) 750 { 751 return ((attributeOptions == null) || 752 attributeOptions.isEmpty()); 753 } 754 else 755 { 756 return false; 757 } 758 } 759 } 760 } 761 762 // It's possible that there could be an attribute without any 763 // values, which we should treat as not having the requested 764 // attribute. 765 for (Attribute a : attributes) 766 { 767 if (a.hasValue() && a.hasOptions(attributeOptions)) 768 { 769 return true; 770 } 771 } 772 773 return false; 774 } 775 776 /** 777 * Retrieves the requested attribute element(s) for the specified 778 * attribute type. The list returned may include multiple elements 779 * if the same attribute exists in the entry multiple times with 780 * different sets of options. It may also include any subordinate 781 * attributes of the attribute being retrieved. 782 * 783 * @param attributeType The attribute type to retrieve. 784 * 785 * @return The requested attribute element(s) for the specified 786 * attribute type, or <CODE>null</CODE> if the specified 787 * attribute type is not present in this entry. 788 */ 789 public List<Attribute> getAttribute(AttributeType attributeType) 790 { 791 return getAttribute(attributeType, true); 792 } 793 794 795 /** 796 * Retrieves the requested attribute element(s) for the specified 797 * attribute type. The list returned may include multiple elements 798 * if the same attribute exists in the entry multiple times with 799 * different sets of options. 800 * 801 * @param attributeType The attribute type to retrieve. 802 * @param includeSubordinates Whether to include any subordinate 803 * attributes of the attribute type 804 * being retrieved. 805 * 806 * @return The requested attribute element(s) for the specified 807 * attribute type, or <CODE>null</CODE> if the specified 808 * attribute type is not present in this entry. 809 */ 810 public List<Attribute> getAttribute(AttributeType attributeType, 811 boolean includeSubordinates) 812 { 813 if (includeSubordinates && 814 attributeType.mayHaveSubordinateTypes()) 815 { 816 List<Attribute> attributes = new LinkedList<Attribute>(); 817 818 List<Attribute> attrs = userAttributes.get(attributeType); 819 if (attrs != null) 820 { 821 attributes.addAll(attrs); 822 } 823 824 attrs = operationalAttributes.get(attributeType); 825 if (attrs != null) 826 { 827 attributes.addAll(attrs); 828 } 829 830 for (AttributeType at : schema.getSubTypes(attributeType)) 831 { 832 attrs = userAttributes.get(at); 833 if (attrs != null) 834 { 835 attributes.addAll(attrs); 836 } 837 838 attrs = operationalAttributes.get(at); 839 if (attrs != null) 840 { 841 attributes.addAll(attrs); 842 } 843 } 844 845 if (attributes.isEmpty()) 846 { 847 return null; 848 } 849 else 850 { 851 return attributes; 852 } 853 } 854 else 855 { 856 List<Attribute> attributes = userAttributes.get(attributeType); 857 858 if (attributes == null) 859 { 860 attributes = operationalAttributes.get(attributeType); 861 if (attributes == null) 862 { 863 if (attributeType.isObjectClassType() && 864 (! objectClasses.isEmpty())) 865 { 866 attributes = new ArrayList<Attribute>(1); 867 attributes.add(getObjectClassAttribute()); 868 return attributes; 869 } 870 else 871 { 872 return null; 873 } 874 } 875 else 876 { 877 return attributes; 878 } 879 } 880 else 881 { 882 return attributes; 883 } 884 } 885 } 886 887 888 889 /** 890 * Retrieves the requested attribute element(s) for the attribute 891 * with the specified name or OID. The list returned may include 892 * multiple elements if the same attribute exists in the entry 893 * multiple times with different sets of options. It may also 894 * include any subordinate attributes of the attribute being 895 * retrieved. 896 * <BR><BR> 897 * Note that this method should only be used in cases in which the 898 * Directory Server schema has no reference of an attribute type 899 * with the specified name. It is not as accurate or efficient as 900 * the version of this method that takes an 901 * <CODE>AttributeType</CODE> argument. 902 * 903 * @param lowerName The name or OID of the attribute to return, 904 * formatted in all lowercase characters. 905 * 906 * @return The requested attribute element(s) for the specified 907 * attribute type, or <CODE>null</CODE> if the specified 908 * attribute type is not present in this entry. 909 */ 910 public List<Attribute> getAttribute(String lowerName) 911 { 912 for (AttributeType attr : userAttributes.keySet()) 913 { 914 if (attr.hasNameOrOID(lowerName)) 915 { 916 return getAttribute(attr, true); 917 } 918 } 919 920 for (AttributeType attr : operationalAttributes.keySet()) 921 { 922 if (attr.hasNameOrOID(lowerName)) 923 { 924 return getAttribute(attr, true); 925 } 926 } 927 928 if (lowerName.equals(OBJECTCLASS_ATTRIBUTE_TYPE_NAME) && 929 (! objectClasses.isEmpty())) 930 { 931 LinkedList<Attribute> attrList = new LinkedList<Attribute>(); 932 attrList.add(getObjectClassAttribute()); 933 return attrList; 934 } 935 936 return null; 937 } 938 939 /** 940 * Retrieves the requested attribute element(s) for the specified 941 * attribute type. The list returned may include multiple elements 942 * if the same attribute exists in the entry multiple times with 943 * different sets of options. It may also include any subordinate 944 * attributes of the attribute being retrieved. 945 * 946 * @param attributeType The attribute type to retrieve. 947 * @param options The set of attribute options to 948 * include in matching elements. 949 * 950 * @return The requested attribute element(s) for the specified 951 * attribute type, or <CODE>null</CODE> if the specified 952 * attribute type is not present in this entry with the 953 * provided set of options. 954 */ 955 public List<Attribute> getAttribute(AttributeType attributeType, 956 Set<String> options) 957 { 958 return getAttribute(attributeType, true, options); 959 } 960 961 /** 962 * Retrieves the requested attribute element(s) for the specified 963 * attribute type. The list returned may include multiple elements 964 * if the same attribute exists in the entry multiple times with 965 * different sets of options. 966 * 967 * @param attributeType The attribute type to retrieve. 968 * @param includeSubordinates Whether to include any subordinate 969 * attributes of the attribute type 970 * being retrieved. 971 * @param options The set of attribute options to 972 * include in matching elements. 973 * 974 * @return The requested attribute element(s) for the specified 975 * attribute type, or <CODE>null</CODE> if the specified 976 * attribute type is not present in this entry with the 977 * provided set of options. 978 */ 979 public List<Attribute> getAttribute(AttributeType attributeType, 980 boolean includeSubordinates, 981 Set<String> options) 982 { 983 List<Attribute> attributes = new LinkedList<Attribute>(); 984 if (includeSubordinates && 985 attributeType.mayHaveSubordinateTypes()) 986 { 987 List<Attribute> attrs = userAttributes.get(attributeType); 988 if (attrs != null) 989 { 990 attributes.addAll(attrs); 991 } 992 993 attrs = operationalAttributes.get(attributeType); 994 if (attrs != null) 995 { 996 attributes.addAll(attrs); 997 } 998 999 for (AttributeType at : schema.getSubTypes(attributeType)) 1000 { 1001 attrs = userAttributes.get(at); 1002 if (attrs != null) 1003 { 1004 attributes.addAll(attrs); 1005 } 1006 1007 attrs = operationalAttributes.get(at); 1008 if (attrs != null) 1009 { 1010 attributes.addAll(attrs); 1011 } 1012 } 1013 } 1014 else 1015 { 1016 List<Attribute> attrs = userAttributes.get(attributeType); 1017 if (attrs == null) 1018 { 1019 attrs = operationalAttributes.get(attributeType); 1020 if (attrs == null) 1021 { 1022 if (attributeType.isObjectClassType() && 1023 (! objectClasses.isEmpty()) && 1024 ((options == null) || options.isEmpty())) 1025 { 1026 attributes.add(getObjectClassAttribute()); 1027 return attributes; 1028 } 1029 else 1030 { 1031 return null; 1032 } 1033 } 1034 else 1035 { 1036 attributes.addAll(attrs); 1037 } 1038 } 1039 else 1040 { 1041 attributes.addAll(attrs); 1042 } 1043 } 1044 1045 1046 Iterator<Attribute> iterator = attributes.iterator(); 1047 while (iterator.hasNext()) 1048 { 1049 Attribute a = iterator.next(); 1050 if (! a.hasOptions(options)) 1051 { 1052 iterator.remove(); 1053 } 1054 } 1055 1056 if (attributes.isEmpty()) 1057 { 1058 return null; 1059 } 1060 else 1061 { 1062 return attributes; 1063 } 1064 } 1065 1066 1067 1068 /** 1069 * Retrieves the requested attribute element(s) for the attribute 1070 * with the specified name or OID and set of options. The list 1071 * returned may include multiple elements if the same attribute 1072 * exists in the entry multiple times with different sets of 1073 * matching options. 1074 * <BR><BR> 1075 * Note that this method should only be used in cases in which the 1076 * Directory Server schema has no reference of an attribute type 1077 * with the specified name. It is not as accurate or efficient as 1078 * the version of this method that takes an 1079 * <CODE>AttributeType</CODE> argument. 1080 * 1081 * @param lowerName The name or OID of the attribute to return, 1082 * formatted in all lowercase characters. 1083 * @param options The set of attribute options to include in 1084 * matching elements. 1085 * 1086 * @return The requested attribute element(s) for the specified 1087 * attribute type, or <CODE>null</CODE> if the specified 1088 * attribute type is not present in this entry. 1089 */ 1090 public List<Attribute> getAttribute(String lowerName, 1091 Set<String> options) 1092 { 1093 for (AttributeType attr : userAttributes.keySet()) 1094 { 1095 if (attr.hasNameOrOID(lowerName)) 1096 { 1097 return getAttribute(attr, options); 1098 } 1099 } 1100 1101 for (AttributeType attr : operationalAttributes.keySet()) 1102 { 1103 if (attr.hasNameOrOID(lowerName)) 1104 { 1105 return getAttribute(attr, options); 1106 } 1107 } 1108 1109 if (lowerName.equals(OBJECTCLASS_ATTRIBUTE_TYPE_NAME) && 1110 ((options == null) || options.isEmpty())) 1111 { 1112 LinkedList<Attribute> attributes = new LinkedList<Attribute>(); 1113 attributes.add(getObjectClassAttribute()); 1114 return attributes; 1115 } 1116 else 1117 { 1118 return null; 1119 } 1120 } 1121 1122 1123 1124 /** 1125 * Retrieves the requested attribute type from the entry and decodes 1126 * a single value as an object of type T. 1127 * <p> 1128 * If the requested attribute type is not present then 1129 * <code>null</code> is returned. If more than one attribute value 1130 * is present, then the first value found will be decoded and 1131 * returned. 1132 * <p> 1133 * The attribute value is decoded using the specified 1134 * {@link org.opends.server.api.AttributeValueDecoder}. 1135 * 1136 * @param <T> 1137 * Decode the attribute value to an object of this type. 1138 * @param attributeType 1139 * The attribute type to retrieve. 1140 * @param decoder 1141 * The attribute value decoder. 1142 * @return The decoded attribute value or <code>null</code> if no 1143 * attribute value having the specified attribute type was 1144 * found. 1145 * @throws DirectoryException 1146 * If the requested attribute value could not be decoded 1147 * successfully. 1148 */ 1149 public final <T> T getAttributeValue(AttributeType attributeType, 1150 AttributeValueDecoder<T> decoder) throws DirectoryException 1151 { 1152 List<Attribute> attributes = getAttribute(attributeType, true); 1153 AttributeValueIterable values = 1154 new AttributeValueIterable(attributes); 1155 Iterator<AttributeValue> iterator = values.iterator(); 1156 1157 if (iterator.hasNext()) 1158 { 1159 return decoder.decode(iterator.next()); 1160 } 1161 else 1162 { 1163 return null; 1164 } 1165 } 1166 1167 1168 1169 /** 1170 * Retrieves the requested attribute type from the entry and decodes 1171 * any values as objects of type T and then places them in the 1172 * specified collection. 1173 * <p> 1174 * If the requested attribute type is not present then no decoded 1175 * values will be added to the container. 1176 * <p> 1177 * The attribute value is decoded using the specified 1178 * {@link org.opends.server.api.AttributeValueDecoder}. 1179 * 1180 * @param <T> 1181 * Decode the attribute values to objects of this type. 1182 * @param attributeType 1183 * The attribute type to retrieve. 1184 * @param decoder 1185 * The attribute value decoder. 1186 * @param collection 1187 * The collection to which decoded values should be added. 1188 * @return The collection containing the decoded attribute value. 1189 * @throws DirectoryException 1190 * If one or more of the requested attribute values could 1191 * not be decoded successfully. 1192 */ 1193 public final <T> Collection<T> getAttributeValues( 1194 AttributeType attributeType, 1195 AttributeValueDecoder<? extends T> decoder, 1196 Collection<T> collection) 1197 throws DirectoryException 1198 { 1199 List<Attribute> attributes = getAttribute(attributeType, true); 1200 AttributeValueIterable values = 1201 new AttributeValueIterable(attributes); 1202 1203 for (AttributeValue value : values) 1204 { 1205 collection.add(decoder.decode(value)); 1206 } 1207 1208 return collection; 1209 } 1210 1211 1212 1213 /** 1214 * Indicates whether this entry contains the specified user 1215 * attribute. 1216 * 1217 * @param attributeType 1218 * The attribute type for which to make the determination. 1219 * @return <CODE>true</CODE> if this entry contains the specified 1220 * user attribute, or <CODE>false</CODE> if not. 1221 */ 1222 public boolean hasUserAttribute(AttributeType attributeType) 1223 { 1224 if (userAttributes.containsKey(attributeType)) 1225 { 1226 return true; 1227 } 1228 1229 if (attributeType.mayHaveSubordinateTypes()) 1230 { 1231 for (AttributeType at : schema.getSubTypes(attributeType)) 1232 { 1233 if (userAttributes.containsKey(at)) 1234 { 1235 return true; 1236 } 1237 } 1238 } 1239 1240 return false; 1241 } 1242 1243 1244 1245 /** 1246 * Retrieves the requested user attribute element(s) for the 1247 * specified attribute type. The list returned may include multiple 1248 * elements if the same attribute exists in the entry multiple times 1249 * with different sets of options. 1250 * 1251 * @param attributeType The attribute type to retrieve. 1252 * 1253 * @return The requested attribute element(s) for the specified 1254 * attribute type, or <CODE>null</CODE> if there is no such 1255 * user attribute. 1256 */ 1257 public List<Attribute> getUserAttribute(AttributeType attributeType) 1258 { 1259 if (attributeType.mayHaveSubordinateTypes()) 1260 { 1261 LinkedList<Attribute> attributes = new LinkedList<Attribute>(); 1262 1263 List<Attribute> attrs = userAttributes.get(attributeType); 1264 if (attrs != null) 1265 { 1266 attributes.addAll(attrs); 1267 } 1268 1269 for (AttributeType at : schema.getSubTypes(attributeType)) 1270 { 1271 attrs = userAttributes.get(at); 1272 if (attrs != null) 1273 { 1274 attributes.addAll(attrs); 1275 } 1276 } 1277 1278 if (attributes.isEmpty()) 1279 { 1280 return null; 1281 } 1282 else 1283 { 1284 return attributes; 1285 } 1286 } 1287 else 1288 { 1289 return userAttributes.get(attributeType); 1290 } 1291 } 1292 1293 1294 1295 /** 1296 * Retrieves the requested user attribute element(s) for the 1297 * specified attribute type. The list returned may include multiple 1298 * elements if the same attribute exists in the entry multiple times 1299 * with different sets of options. 1300 * 1301 * @param attributeType The attribute type to retrieve. 1302 * @param options The set of attribute options to include in 1303 * matching elements. 1304 * 1305 * @return The requested attribute element(s) for the specified 1306 * attribute type, or <CODE>null</CODE> if there is no such 1307 * user attribute with the specified set of options. 1308 */ 1309 public List<Attribute> getUserAttribute(AttributeType attributeType, 1310 Set<String> options) 1311 { 1312 LinkedList<Attribute> attributes = new LinkedList<Attribute>(); 1313 List<Attribute> attrs = userAttributes.get(attributeType); 1314 if (attrs != null) 1315 { 1316 attributes.addAll(attrs); 1317 } 1318 1319 if (attributeType.mayHaveSubordinateTypes()) 1320 { 1321 for (AttributeType at : schema.getSubTypes(attributeType)) 1322 { 1323 attrs = userAttributes.get(at); 1324 if (attrs != null) 1325 { 1326 attributes.addAll(attrs); 1327 } 1328 } 1329 } 1330 1331 Iterator<Attribute> iterator = attributes.iterator(); 1332 while (iterator.hasNext()) 1333 { 1334 Attribute a = iterator.next(); 1335 if (! a.hasOptions(options)) 1336 { 1337 iterator.remove(); 1338 } 1339 } 1340 1341 if (attributes.isEmpty()) 1342 { 1343 return null; 1344 } 1345 else 1346 { 1347 return attributes; 1348 } 1349 } 1350 1351 1352 1353 /** 1354 * Retrieves a duplicate of the user attribute list for the 1355 * specified type. 1356 * 1357 * @param attributeType The attribute type for which to retrieve a 1358 * duplicate attribute list. 1359 * 1360 * @return A duplicate of the requested attribute list, or 1361 * <CODE>null</CODE> if there is no such user attribute. 1362 */ 1363 public List<Attribute> duplicateUserAttribute( 1364 AttributeType attributeType) 1365 { 1366 LinkedList<Attribute> attributes = new LinkedList<Attribute>(); 1367 1368 List<Attribute> attrs = userAttributes.get(attributeType); 1369 if (attrs != null) 1370 { 1371 for (Attribute a : attrs) 1372 { 1373 attributes.add(a.duplicate()); 1374 } 1375 } 1376 1377 if (attributeType.mayHaveSubordinateTypes()) 1378 { 1379 for (AttributeType at : schema.getSubTypes(attributeType)) 1380 { 1381 attrs = userAttributes.get(at); 1382 if (attrs != null) 1383 { 1384 for (Attribute a : attrs) 1385 { 1386 attributes.add(a.duplicate()); 1387 } 1388 } 1389 } 1390 } 1391 1392 if (attributes.isEmpty()) 1393 { 1394 return null; 1395 } 1396 else 1397 { 1398 return attributes; 1399 } 1400 } 1401 1402 1403 1404 /** 1405 * Makes a copy of attributes matching the specified options. 1406 * 1407 * @param attrList The attributes to be copied. 1408 * @param options The set of attribute options to include in 1409 * matching elements. 1410 * @param omitValues <CODE>true</CODE> if the values are to be 1411 * omitted. 1412 * 1413 * @return A copy of the attributes matching the specified options, 1414 * or <CODE>null</CODE> if there is no such attribute with 1415 * the specified set of options. 1416 */ 1417 private static List<Attribute> duplicateAttribute( 1418 List<Attribute> attrList, 1419 Set<String> options, 1420 boolean omitValues) 1421 { 1422 if (attrList == null) 1423 { 1424 return null; 1425 } 1426 1427 ArrayList<Attribute> duplicateList = 1428 new ArrayList<Attribute>(attrList.size()); 1429 for (Attribute a : attrList) 1430 { 1431 if (a.hasOptions(options)) 1432 { 1433 duplicateList.add(a.duplicate(omitValues)); 1434 } 1435 } 1436 1437 if (duplicateList.isEmpty()) 1438 { 1439 return null; 1440 } 1441 else 1442 { 1443 return duplicateList; 1444 } 1445 } 1446 1447 1448 1449 /** 1450 * Retrieves a copy of the requested user attribute element(s) for 1451 * the specified attribute type. The list returned may include 1452 * multiple elements if the same attribute exists in the entry 1453 * multiple times with different sets of options. 1454 * 1455 * @param attributeType The attribute type to retrieve. 1456 * @param options The set of attribute options to include in 1457 * matching elements. 1458 * @param omitValues <CODE>true</CODE> if the values are to be 1459 * omitted. 1460 * 1461 * @return A copy of the requested attribute element(s) for the 1462 * specified attribute type, or <CODE>null</CODE> if there 1463 * is no such user attribute with the specified set of 1464 * options. 1465 */ 1466 public List<Attribute> duplicateUserAttribute( 1467 AttributeType attributeType, 1468 Set<String> options, 1469 boolean omitValues) 1470 { 1471 List<Attribute> currentList = getUserAttribute(attributeType); 1472 return duplicateAttribute(currentList, options, omitValues); 1473 } 1474 1475 1476 1477 /** 1478 * Retrieves a copy of the requested operational attribute 1479 * element(s) for the specified attribute type. The list returned 1480 * may include multiple elements if the same attribute exists in 1481 * the entry multiple times with different sets of options. 1482 * 1483 * @param attributeType The attribute type to retrieve. 1484 * @param options The set of attribute options to include in 1485 * matching elements. 1486 * @param omitValues <CODE>true</CODE> if the values are to be 1487 * omitted. 1488 * 1489 * @return A copy of the requested attribute element(s) for the 1490 * specified attribute type, or <CODE>null</CODE> if there 1491 * is no such user attribute with the specified set of 1492 * options. 1493 */ 1494 public List<Attribute> duplicateOperationalAttribute( 1495 AttributeType attributeType, 1496 Set<String> options, 1497 boolean omitValues) 1498 { 1499 List<Attribute> currentList = 1500 getOperationalAttribute(attributeType); 1501 return duplicateAttribute(currentList, options, omitValues); 1502 } 1503 1504 1505 /** 1506 * Indicates whether this entry contains the specified operational 1507 * attribute. 1508 * 1509 * @param attributeType The attribute type for which to make the 1510 * determination. 1511 * 1512 * @return <CODE>true</CODE> if this entry contains the specified 1513 * operational attribute, or <CODE>false</CODE> if not. 1514 */ 1515 public boolean hasOperationalAttribute(AttributeType attributeType) 1516 { 1517 if (operationalAttributes.containsKey(attributeType)) 1518 { 1519 return true; 1520 } 1521 1522 if (attributeType.mayHaveSubordinateTypes()) 1523 { 1524 for (AttributeType at : schema.getSubTypes(attributeType)) 1525 { 1526 if (operationalAttributes.containsKey(at)) 1527 { 1528 return true; 1529 } 1530 } 1531 } 1532 1533 return false; 1534 } 1535 1536 1537 1538 /** 1539 * Retrieves the requested operational attribute element(s) for the 1540 * specified attribute type. The list returned may include multiple 1541 * elements if the same attribute exists in the entry multiple times 1542 * with different sets of options. 1543 * 1544 * @param attributeType The attribute type to retrieve. 1545 * 1546 * @return The requested attribute element(s) for the specified 1547 * attribute type, or <CODE>null</CODE> if there is no such 1548 * operational attribute. 1549 */ 1550 public List<Attribute> getOperationalAttribute( 1551 AttributeType attributeType) 1552 { 1553 if (attributeType.mayHaveSubordinateTypes()) 1554 { 1555 LinkedList<Attribute> attributes = new LinkedList<Attribute>(); 1556 1557 List<Attribute> attrs = 1558 operationalAttributes.get(attributeType); 1559 if (attrs != null) 1560 { 1561 attributes.addAll(attrs); 1562 } 1563 1564 for (AttributeType at : schema.getSubTypes(attributeType)) 1565 { 1566 attrs = operationalAttributes.get(at); 1567 if (attrs != null) 1568 { 1569 attributes.addAll(attrs); 1570 } 1571 } 1572 1573 if (attributes.isEmpty()) 1574 { 1575 return null; 1576 } 1577 else 1578 { 1579 return attributes; 1580 } 1581 } 1582 else 1583 { 1584 return operationalAttributes.get(attributeType); 1585 } 1586 } 1587 1588 1589 1590 /** 1591 * Retrieves the requested operational attribute element(s) for the 1592 * specified attribute type. The list returned may include multiple 1593 * elements if the same attribute exists in the entry multiple times 1594 * with different sets of options. 1595 * 1596 * @param attributeType The attribute type to retrieve. 1597 * @param options The set of attribute options to include in 1598 * matching elements. 1599 * 1600 * @return The requested attribute element(s) for the specified 1601 * attribute type, or <CODE>null</CODE> if there is no such 1602 * operational attribute with the specified set of options. 1603 */ 1604 public List<Attribute> getOperationalAttribute( 1605 AttributeType attributeType, 1606 Set<String> options) 1607 { 1608 LinkedList<Attribute> attributes = new LinkedList<Attribute>(); 1609 List<Attribute> attrs = operationalAttributes.get(attributeType); 1610 if (attrs != null) 1611 { 1612 attributes.addAll(attrs); 1613 } 1614 1615 if (attributeType.mayHaveSubordinateTypes()) 1616 { 1617 for (AttributeType at : schema.getSubTypes(attributeType)) 1618 { 1619 attrs = operationalAttributes.get(at); 1620 if (attrs != null) 1621 { 1622 attributes.addAll(attrs); 1623 } 1624 } 1625 } 1626 1627 Iterator<Attribute> iterator = attributes.iterator(); 1628 while (iterator.hasNext()) 1629 { 1630 Attribute a = iterator.next(); 1631 if (! a.hasOptions(options)) 1632 { 1633 iterator.remove(); 1634 } 1635 } 1636 1637 if (attributes.isEmpty()) 1638 { 1639 return null; 1640 } 1641 else 1642 { 1643 return attributes; 1644 } 1645 } 1646 1647 1648 1649 /** 1650 * Retrieves a duplicate of the operational attribute list for the 1651 * specified type. 1652 * 1653 * @param attributeType The attribute type for which to retrieve a 1654 * duplicate attribute list. 1655 * 1656 * @return A duplicate of the requested attribute list, or 1657 * <CODE>null</CODE> if there is no such operational 1658 * attribute. 1659 */ 1660 public List<Attribute> duplicateOperationalAttribute( 1661 AttributeType attributeType) 1662 { 1663 LinkedList<Attribute> attributes = new LinkedList<Attribute>(); 1664 1665 List<Attribute> attrs = operationalAttributes.get(attributeType); 1666 if (attrs != null) 1667 { 1668 for (Attribute a : attrs) 1669 { 1670 attributes.add(a.duplicate()); 1671 } 1672 } 1673 1674 if (attributeType.mayHaveSubordinateTypes()) 1675 { 1676 for (AttributeType at : schema.getSubTypes(attributeType)) 1677 { 1678 attrs = operationalAttributes.get(at); 1679 if (attrs != null) 1680 { 1681 for (Attribute a : attrs) 1682 { 1683 attributes.add(a.duplicate()); 1684 } 1685 } 1686 } 1687 } 1688 1689 if (attributes.isEmpty()) 1690 { 1691 return null; 1692 } 1693 else 1694 { 1695 return attributes; 1696 } 1697 } 1698 1699 1700 /** 1701 * Puts the provided attribute in this entry. If an attribute 1702 * already exists with the provided type, it will be overwritten. 1703 * Otherwise, a new attribute will be added. Note that no 1704 * validation will be performed. 1705 * 1706 * @param attributeType The attribute type for the set of 1707 * attributes to add. 1708 * @param attributeList The set of attributes to add for the given 1709 * type. 1710 */ 1711 public void putAttribute(AttributeType attributeType, 1712 List<Attribute> attributeList) 1713 { 1714 attachment = null; 1715 1716 1717 // See if there is already a set of attributes with the specified 1718 // type. If so, then overwrite it. 1719 List<Attribute> attrList = userAttributes.get(attributeType); 1720 if (attrList != null) 1721 { 1722 userAttributes.put(attributeType, attributeList); 1723 return; 1724 } 1725 1726 attrList = operationalAttributes.get(attributeType); 1727 if (attrList != null) 1728 { 1729 operationalAttributes.put(attributeType, attributeList); 1730 return; 1731 } 1732 1733 1734 // This is a new attribute, so add it to the set of user or 1735 // operational attributes as appropriate. 1736 if (attributeType.isOperational()) 1737 { 1738 operationalAttributes.put(attributeType, attributeList); 1739 } 1740 else 1741 { 1742 userAttributes.put(attributeType, attributeList); 1743 } 1744 } 1745 1746 1747 1748 /** 1749 * Adds the provided attribute to this entry. If an attribute with 1750 * the provided type already exists, then the values will be merged. 1751 * 1752 * @param attribute The attribute to add or merge with this 1753 * entry. 1754 * @param duplicateValues A list to which any duplicate values 1755 * will be added. 1756 */ 1757 public void addAttribute(Attribute attribute, 1758 List<AttributeValue> duplicateValues) 1759 { 1760 attachment = null; 1761 1762 List<Attribute> attrList = 1763 getAttribute(attribute.getAttributeType(), false); 1764 if (attrList == null) 1765 { 1766 // There are no instances of the specified attribute in this 1767 // entry, so simply add it. 1768 attrList = new ArrayList<Attribute>(1); 1769 attrList.add(attribute); 1770 1771 AttributeType attrType = attribute.getAttributeType(); 1772 if (attrType.isOperational()) 1773 { 1774 operationalAttributes.put(attrType, attrList); 1775 } 1776 else 1777 { 1778 userAttributes.put(attrType, attrList); 1779 } 1780 1781 return; 1782 } 1783 else 1784 { 1785 // There are some instances of this attribute, but they may not 1786 // have exactly the same set of options. See if we can find an 1787 // attribute with the same set of options to merge in the 1788 // values. If not, then add the new attribute to the list. 1789 HashSet<String> options = attribute.getOptions(); 1790 for (Attribute a : attrList) 1791 { 1792 if (a.optionsEqual(options)) 1793 { 1794 // There is an attribute with the same set of options. 1795 // Merge the value lists together. 1796 LinkedHashSet<AttributeValue> existingValues = 1797 a.getValues(); 1798 LinkedHashSet<AttributeValue> newValues = 1799 attribute.getValues(); 1800 for (AttributeValue v : newValues) 1801 { 1802 if (! existingValues.add(v)) 1803 { 1804 duplicateValues.add(v); 1805 } 1806 } 1807 1808 return; 1809 } 1810 } 1811 1812 attrList.add(attribute); 1813 } 1814 } 1815 1816 1817 1818 /** 1819 * Removes all instances of the specified attribute type from this 1820 * entry, including any instances with options. If the provided 1821 * attribute type is the objectclass type, then all objectclass 1822 * values will be removed (but must be replaced for the entry to 1823 * be valid). If the specified attribute type is not present in 1824 * this entry, then this method will have no effect. 1825 * 1826 * @param attributeType The attribute type for the attribute to 1827 * remove from this entry. 1828 * 1829 * @return <CODE>true</CODE> if the attribute was found and 1830 * removed, or <CODE>false</CODE> if it was not present in 1831 * the entry. 1832 */ 1833 public boolean removeAttribute(AttributeType attributeType) 1834 { 1835 attachment = null; 1836 1837 if (attributeType.isObjectClassType()) 1838 { 1839 objectClasses.clear(); 1840 return true; 1841 } 1842 else 1843 { 1844 return ((userAttributes.remove(attributeType) != null) || 1845 (operationalAttributes.remove(attributeType) != null)); 1846 } 1847 } 1848 1849 1850 1851 /** 1852 * Removes the attribute with the provided type and set of options 1853 * from this entry. Only the instance with the exact set of 1854 * options provided will be removed. This has no effect if the 1855 * specified attribute is not present in this entry with the given 1856 * set of options. 1857 * 1858 * @param attributeType The attribute type for the attribute to 1859 * remove from this entry. 1860 * @param options The set of attribute options to use when 1861 * determining which attribute to remove. 1862 * 1863 * @return <CODE>true</CODE> if the attribute was found and 1864 * removed, or <CODE>false</CODE> if it was not present in 1865 * the entry. 1866 */ 1867 public boolean removeAttribute(AttributeType attributeType, 1868 Set<String> options) 1869 { 1870 attachment = null; 1871 1872 List<Attribute> attrList = userAttributes.get(attributeType); 1873 if (attrList == null) 1874 { 1875 attrList = operationalAttributes.get(attributeType); 1876 if (attrList == null) 1877 { 1878 return false; 1879 } 1880 } 1881 1882 boolean removed = false; 1883 1884 Iterator<Attribute> iterator = attrList.iterator(); 1885 while (iterator.hasNext()) 1886 { 1887 Attribute a = iterator.next(); 1888 if (a.optionsEqual(options)) 1889 { 1890 iterator.remove(); 1891 removed = true; 1892 break; 1893 } 1894 } 1895 1896 if (attrList.isEmpty()) 1897 { 1898 userAttributes.remove(attributeType); 1899 operationalAttributes.remove(attributeType); 1900 } 1901 1902 return removed; 1903 } 1904 1905 1906 1907 /** 1908 * Removes the provided attribute from this entry. If the given 1909 * attribute does not have any values, then all values of the 1910 * associated attribute type (taking into account the options in the 1911 * provided type) will be removed. Otherwise, only the specified 1912 * values will be removed. 1913 * 1914 * @param attribute The attribute containing the information 1915 * to use to perform the removal. 1916 * @param missingValues A list to which any values contained in 1917 * the provided attribute but not present in 1918 * the entry will be added. 1919 * 1920 * @return <CODE>true</CODE> if the attribute type was present and 1921 * the specified values that were present were removed, or 1922 * <CODE>false</CODE> if the attribute type was not present 1923 * in the entry. If the attribute type was present but 1924 * only contained some of the values in the provided 1925 * attribute, then this method will return 1926 * <CODE>true</CODE> but will add those values to the 1927 * provided list. 1928 */ 1929 public boolean removeAttribute(Attribute attribute, 1930 List<AttributeValue> missingValues) 1931 { 1932 attachment = null; 1933 1934 1935 if (attribute.getAttributeType().isObjectClassType()) 1936 { 1937 LinkedHashSet<AttributeValue> valueSet = attribute.getValues(); 1938 if ((valueSet == null) || valueSet.isEmpty()) 1939 { 1940 objectClasses.clear(); 1941 return true; 1942 } 1943 1944 boolean allSuccessful = true; 1945 1946 for (AttributeValue v : attribute.getValues()) 1947 { 1948 String ocName; 1949 try 1950 { 1951 ocName = v.getNormalizedStringValue(); 1952 } 1953 catch (Exception e) 1954 { 1955 if (debugEnabled()) 1956 { 1957 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1958 } 1959 1960 ocName = toLowerCase(v.getStringValue()); 1961 } 1962 1963 boolean matchFound = false; 1964 1965 for (ObjectClass oc : objectClasses.keySet()) 1966 { 1967 if (oc.hasNameOrOID(ocName)) 1968 { 1969 matchFound = true; 1970 objectClasses.remove(oc); 1971 break; 1972 } 1973 } 1974 1975 if (! matchFound) 1976 { 1977 allSuccessful = false; 1978 missingValues.add(v); 1979 } 1980 } 1981 1982 return allSuccessful; 1983 } 1984 1985 1986 if (attribute.hasOptions()) 1987 { 1988 HashSet<String> options = attribute.getOptions(); 1989 1990 LinkedHashSet<AttributeValue> valueSet = attribute.getValues(); 1991 if ((valueSet == null) || valueSet.isEmpty()) 1992 { 1993 return removeAttribute(attribute.getAttributeType(), options); 1994 } 1995 1996 List<Attribute> attrList = 1997 getAttribute(attribute.getAttributeType(), false); 1998 if (attrList == null) 1999 { 2000 return false; 2001 } 2002 2003 for (Attribute a : attrList) 2004 { 2005 if (a.optionsEqual(options)) 2006 { 2007 LinkedHashSet<AttributeValue> existingValueSet = 2008 a.getValues(); 2009 2010 for (AttributeValue v : valueSet) 2011 { 2012 if (! existingValueSet.remove(v)) 2013 { 2014 missingValues.add(v); 2015 } 2016 } 2017 2018 if (existingValueSet.isEmpty()) 2019 { 2020 return removeAttribute(attribute.getAttributeType(), 2021 options); 2022 } 2023 2024 return true; 2025 } 2026 } 2027 2028 return false; 2029 } 2030 else 2031 { 2032 LinkedHashSet<AttributeValue> valueSet = attribute.getValues(); 2033 if ((valueSet == null) || valueSet.isEmpty()) 2034 { 2035 return removeAttribute(attribute.getAttributeType(), null); 2036 } 2037 2038 List<Attribute> attrList = 2039 getAttribute(attribute.getAttributeType(), false); 2040 if (attrList == null) 2041 { 2042 return false; 2043 } 2044 2045 for (Attribute a : attrList) 2046 { 2047 if (! a.hasOptions()) 2048 { 2049 LinkedHashSet<AttributeValue> existingValueSet = 2050 a.getValues(); 2051 2052 for (AttributeValue v : valueSet) 2053 { 2054 if (! existingValueSet.remove(v)) 2055 { 2056 missingValues.add(v); 2057 } 2058 } 2059 2060 if (existingValueSet.isEmpty()) 2061 { 2062 return removeAttribute(attribute.getAttributeType(), 2063 null); 2064 } 2065 2066 return true; 2067 } 2068 } 2069 2070 return false; 2071 } 2072 } 2073 2074 2075 2076 /** 2077 * Indicates whether the specified attribute type is allowed by any 2078 * of the objectclasses associated with this entry. 2079 * 2080 * @param attributeType The attribute type for which to make the 2081 * determination. 2082 * 2083 * @return <CODE>true</CODE> if the specified attribute is allowed 2084 * by any of the objectclasses associated with this entry, 2085 * or <CODE>false</CODE> if it is not. 2086 */ 2087 public boolean allowsAttribute(AttributeType attributeType) 2088 { 2089 for (ObjectClass o : objectClasses.keySet()) 2090 { 2091 if (o.isRequiredOrOptional(attributeType)) 2092 { 2093 return true; 2094 } 2095 } 2096 2097 return false; 2098 } 2099 2100 2101 2102 /** 2103 * Indicates whether the specified attribute type is required by any 2104 * of the objectclasses associated with this entry. 2105 * 2106 * @param attributeType The attribute type for which to make the 2107 * determination. 2108 * 2109 * @return <CODE>true</CODE> if the specified attribute is required 2110 * by any of the objectclasses associated with this entry, 2111 * o r<CODE>false</CODE> if it is not. 2112 */ 2113 public boolean requiresAttribute(AttributeType attributeType) 2114 { 2115 for (ObjectClass o : objectClasses.keySet()) 2116 { 2117 if (o.isRequired(attributeType)) 2118 { 2119 return true; 2120 } 2121 } 2122 2123 return false; 2124 } 2125 2126 2127 2128 /** 2129 * Indicates whether this entry contains the specified attribute 2130 * value. 2131 * 2132 * @param attributeType The attribute type for the attribute. 2133 * @param options The set of options for the attribute. 2134 * @param value The value for the attribute. 2135 * 2136 * @return <CODE>true</CODE> if this entry contains the specified 2137 * attribute value, or <CODE>false</CODE> if it does not. 2138 */ 2139 public boolean hasValue(AttributeType attributeType, 2140 Set<String> options, AttributeValue value) 2141 { 2142 List<Attribute> attrList = getAttribute(attributeType, true); 2143 if ((attrList == null) || attrList.isEmpty()) 2144 { 2145 return false; 2146 } 2147 2148 for (Attribute a : attrList) 2149 { 2150 if (a.optionsEqual(options)) 2151 { 2152 return a.hasValue(value); 2153 } 2154 } 2155 2156 return false; 2157 } 2158 2159 2160 2161 /** 2162 * Applies the provided modification to this entry. No schema 2163 * checking will be performed. 2164 * 2165 * @param mod The modification to apply to this entry. 2166 * 2167 * @throws DirectoryException If a problem occurs while attempting 2168 * to apply the modification. Note 2169 * that even if a problem occurs, then 2170 * the entry may have been altered in 2171 * some way. 2172 */ 2173 public void applyModification(Modification mod) 2174 throws DirectoryException 2175 { 2176 Attribute a = mod.getAttribute(); 2177 AttributeType t = a.getAttributeType(); 2178 2179 // We'll need to handle changes to the objectclass attribute in a 2180 // special way. 2181 if (t.isObjectClassType()) 2182 { 2183 LinkedHashMap<ObjectClass,String> ocs = new 2184 LinkedHashMap<ObjectClass,String>(); 2185 for (AttributeValue v : a.getValues()) 2186 { 2187 String ocName = v.getStringValue(); 2188 String lowerName = toLowerCase(ocName); 2189 ObjectClass oc = 2190 DirectoryServer.getObjectClass(lowerName, true); 2191 ocs.put(oc, ocName); 2192 } 2193 2194 switch (mod.getModificationType()) 2195 { 2196 case ADD: 2197 for (ObjectClass oc : ocs.keySet()) 2198 { 2199 if (objectClasses.containsKey(oc)) 2200 { 2201 Message message = 2202 ERR_ENTRY_DUPLICATE_VALUES.get(a.getName()); 2203 throw new DirectoryException( 2204 ResultCode.ATTRIBUTE_OR_VALUE_EXISTS, 2205 message); 2206 } 2207 else 2208 { 2209 objectClasses.put(oc, ocs.get(oc)); 2210 } 2211 } 2212 break; 2213 2214 case DELETE: 2215 for (ObjectClass oc : ocs.keySet()) 2216 { 2217 if (objectClasses.remove(oc) == null) 2218 { 2219 Message message = 2220 ERR_ENTRY_NO_SUCH_VALUE.get(a.getName()); 2221 throw new DirectoryException( 2222 ResultCode.NO_SUCH_ATTRIBUTE, message); 2223 } 2224 } 2225 break; 2226 2227 case REPLACE: 2228 objectClasses = ocs; 2229 break; 2230 2231 case INCREMENT: 2232 Message message = 2233 ERR_ENTRY_OC_INCREMENT_NOT_SUPPORTED.get(); 2234 throw new DirectoryException( 2235 ResultCode.UNWILLING_TO_PERFORM, message); 2236 2237 default: 2238 message = ERR_ENTRY_UNKNOWN_MODIFICATION_TYPE.get( 2239 String.valueOf(mod.getModificationType())); 2240 throw new DirectoryException( 2241 ResultCode.UNWILLING_TO_PERFORM, message); 2242 } 2243 2244 return; 2245 } 2246 2247 switch (mod.getModificationType()) 2248 { 2249 case ADD: 2250 LinkedList<AttributeValue> duplicateValues = 2251 new LinkedList<AttributeValue>(); 2252 addAttribute(a, duplicateValues); 2253 if (! duplicateValues.isEmpty()) 2254 { 2255 Message message = 2256 ERR_ENTRY_DUPLICATE_VALUES.get(a.getName()); 2257 throw new DirectoryException( 2258 ResultCode.ATTRIBUTE_OR_VALUE_EXISTS, 2259 message); 2260 } 2261 break; 2262 2263 case DELETE: 2264 LinkedList<AttributeValue> missingValues = 2265 new LinkedList<AttributeValue>(); 2266 removeAttribute(a, missingValues); 2267 if (! missingValues.isEmpty()) 2268 { 2269 Message message = ERR_ENTRY_NO_SUCH_VALUE.get(a.getName()); 2270 throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE, 2271 message); 2272 } 2273 break; 2274 2275 case REPLACE: 2276 removeAttribute(t, a.getOptions()); 2277 2278 if (a.hasValue()) 2279 { 2280 // We know that we won't have any duplicate values, so we 2281 // don't kneed to worry about checking for them. 2282 duplicateValues = new LinkedList<AttributeValue>(); 2283 addAttribute(a, duplicateValues); 2284 } 2285 break; 2286 2287 case INCREMENT: 2288 List<Attribute> attrList = getAttribute(t, false); 2289 if ((attrList == null) || attrList.isEmpty()) 2290 { 2291 Message message = 2292 ERR_ENTRY_INCREMENT_NO_SUCH_ATTRIBUTE.get(a.getName()); 2293 throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE, 2294 message); 2295 } 2296 else if (attrList.size() != 1) 2297 { 2298 Message message = 2299 ERR_ENTRY_INCREMENT_MULTIPLE_VALUES.get(a.getName()); 2300 throw new DirectoryException( 2301 ResultCode.CONSTRAINT_VIOLATION, message); 2302 } 2303 2304 LinkedHashSet<AttributeValue> values = 2305 attrList.get(0).getValues(); 2306 if (values.isEmpty()) 2307 { 2308 Message message = 2309 ERR_ENTRY_INCREMENT_NO_SUCH_ATTRIBUTE.get(a.getName()); 2310 throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE, 2311 message); 2312 } 2313 else if (values.size() > 1) 2314 { 2315 Message message = 2316 ERR_ENTRY_INCREMENT_MULTIPLE_VALUES.get(a.getName()); 2317 throw new DirectoryException( 2318 ResultCode.CONSTRAINT_VIOLATION, message); 2319 } 2320 2321 LinkedHashSet<AttributeValue> newValues = a.getValues(); 2322 if (newValues.size() != 1) 2323 { 2324 Message message = ERR_ENTRY_INCREMENT_INVALID_VALUE_COUNT. 2325 get(a.getName()); 2326 throw new DirectoryException( 2327 ResultCode.CONSTRAINT_VIOLATION, message); 2328 } 2329 2330 long newValue; 2331 try 2332 { 2333 String s = values.iterator().next().getStringValue(); 2334 long currentValue = Long.parseLong(s); 2335 2336 s = a.getValues().iterator().next().getStringValue(); 2337 long increment = Long.parseLong(s); 2338 2339 newValue = currentValue+increment; 2340 } 2341 catch (NumberFormatException nfe) 2342 { 2343 Message message = ERR_ENTRY_INCREMENT_CANNOT_PARSE_AS_INT. 2344 get(a.getName()); 2345 throw new DirectoryException( 2346 ResultCode.CONSTRAINT_VIOLATION, message); 2347 } 2348 2349 values.clear(); 2350 values.add(new AttributeValue(t, String.valueOf(newValue))); 2351 break; 2352 2353 default: 2354 Message message = ERR_ENTRY_UNKNOWN_MODIFICATION_TYPE.get( 2355 String.valueOf(mod.getModificationType())); 2356 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 2357 message); 2358 } 2359 } 2360 2361 2362 2363 /** 2364 * Applies all of the provided modifications to this entry. 2365 * 2366 * @param mods The modifications to apply to this entry. 2367 * 2368 * @throws DirectoryException If a problem occurs while attempting 2369 * to apply the modifications. Note 2370 * that even if a problem occurs, then 2371 * the entry may have been altered in 2372 * some way. 2373 */ 2374 public void applyModifications(List<Modification> mods) 2375 throws DirectoryException 2376 { 2377 for (Modification m : mods) 2378 { 2379 applyModification(m); 2380 } 2381 } 2382 2383 2384 2385 /** 2386 * Indicates whether this entry conforms to the server's schema 2387 * requirements. The checks performed by this method include: 2388 * 2389 * <UL> 2390 * <LI>Make sure that all required attributes are present, either 2391 * in the list of user or operational attributes.</LI> 2392 * <LI>Make sure that all user attributes are allowed by at least 2393 * one of the objectclasses. The operational attributes will 2394 * not be checked in this manner.</LI> 2395 * <LI>Make sure that all single-valued attributes contained in 2396 * the entry have only a single value.</LI> 2397 * <LI>Make sure that the entry contains a single structural 2398 * objectclass.</LI> 2399 * <LI>Make sure that the entry complies with any defined name 2400 * forms, DIT content rules, and DIT structure rules.</LI> 2401 * </UL> 2402 * 2403 * @param parentEntry The entry that is the immediate 2404 * parent of this entry, which may 2405 * be checked for DIT structure rule 2406 * conformance. This may be 2407 * {@code null} if there is no 2408 * parent or if it is unavailable 2409 * to the caller. 2410 * @param parentProvided Indicates whether the caller 2411 * attempted to provide the parent. 2412 * If not, then the parent entry 2413 * will be loaded on demand if it is 2414 * required. 2415 * @param validateNameForms Indicates whether to validate the 2416 * entry against name form 2417 * definitions. This should only be 2418 * {@code true} for add and modify 2419 * DN operations, as well as for 2420 * for imports. 2421 * @param validateStructureRules Indicates whether to validate the 2422 * entry against DIT structure rule 2423 * definitions. This should only 2424 * be {@code true} for add and 2425 * modify DN operations. 2426 * @param invalidReason The buffer to which an 2427 * explanation will be appended if 2428 * this entry does not conform to 2429 * the server's schema 2430 * configuration. 2431 * 2432 * @return {@code true} if this entry conforms to the server's 2433 * schema requirements, or {@code false} if it does not. 2434 */ 2435 public boolean conformsToSchema(Entry parentEntry, 2436 boolean parentProvided, 2437 boolean validateNameForms, 2438 boolean validateStructureRules, 2439 MessageBuilder invalidReason) 2440 { 2441 // Get the structural objectclass for the entry. If there isn't 2442 // one, or if there's more than one, then see if that's OK. 2443 AcceptRejectWarn structuralPolicy = 2444 DirectoryServer.getSingleStructuralObjectClassPolicy(); 2445 ObjectClass structuralClass = null; 2446 boolean multipleOCErrorLogged = false; 2447 for (ObjectClass oc : objectClasses.keySet()) 2448 { 2449 if (oc.getObjectClassType() == ObjectClassType.STRUCTURAL) 2450 { 2451 if ((structuralClass == null) || 2452 oc.isDescendantOf(structuralClass)) 2453 { 2454 structuralClass = oc; 2455 } 2456 else if (! structuralClass.isDescendantOf(oc)) 2457 { 2458 Message message = 2459 ERR_ENTRY_SCHEMA_MULTIPLE_STRUCTURAL_CLASSES.get( 2460 String.valueOf(dn), 2461 structuralClass.getNameOrOID(), 2462 oc.getNameOrOID()); 2463 2464 if (structuralPolicy == AcceptRejectWarn.REJECT) 2465 { 2466 invalidReason.append(message); 2467 return false; 2468 } 2469 else if (structuralPolicy == AcceptRejectWarn.WARN) 2470 { 2471 if (! multipleOCErrorLogged) 2472 { 2473 logError(message); 2474 multipleOCErrorLogged = true; 2475 } 2476 } 2477 } 2478 } 2479 } 2480 2481 NameForm nameForm = null; 2482 DITContentRule ditContentRule = null; 2483 DITStructureRule ditStructureRule = null; 2484 if (structuralClass == null) 2485 { 2486 Message message = ERR_ENTRY_SCHEMA_NO_STRUCTURAL_CLASS.get( 2487 String.valueOf(dn)); 2488 2489 if (structuralPolicy == AcceptRejectWarn.REJECT) 2490 { 2491 invalidReason.append(message); 2492 return false; 2493 } 2494 else if (structuralPolicy == AcceptRejectWarn.WARN) 2495 { 2496 logError(message); 2497 } 2498 } 2499 else 2500 { 2501 ditContentRule = 2502 DirectoryServer.getDITContentRule(structuralClass); 2503 if ((ditContentRule != null) && ditContentRule.isObsolete()) 2504 { 2505 ditContentRule = null; 2506 } 2507 2508 if (validateNameForms) 2509 { 2510 nameForm = DirectoryServer.getNameForm(structuralClass); 2511 if ((nameForm != null) && nameForm.isObsolete()) 2512 { 2513 nameForm = null; 2514 } 2515 2516 if (validateStructureRules && (nameForm != null)) 2517 { 2518 ditStructureRule = 2519 DirectoryServer.getDITStructureRule(nameForm); 2520 if ((ditStructureRule != null) && 2521 ditStructureRule.isObsolete()) 2522 { 2523 ditStructureRule = null; 2524 } 2525 } 2526 } 2527 } 2528 2529 2530 if (! checkAttributesAndObjectClasses(ditContentRule, 2531 structuralPolicy, invalidReason)) 2532 { 2533 return false; 2534 } 2535 2536 2537 // If there is a name form for this entry, then make sure that the 2538 // RDN for the entry is in compliance with it. 2539 if (nameForm != null) 2540 { 2541 if (! checkNameForm(nameForm, structuralPolicy, invalidReason)) 2542 { 2543 return false; 2544 } 2545 } 2546 2547 2548 // If there is a DIT content rule for this entry, then make sure 2549 // that the entry is in compliance with it. 2550 if (ditContentRule != null) 2551 { 2552 if (! checkDITContentRule(ditContentRule, structuralPolicy, 2553 invalidReason)) 2554 { 2555 return false; 2556 } 2557 } 2558 2559 2560 if (! checkDITStructureRule(ditStructureRule, structuralClass, 2561 parentEntry, parentProvided, validateStructureRules, 2562 structuralPolicy, invalidReason)) 2563 { 2564 return false; 2565 } 2566 2567 2568 // If we've gotten here, then the entry is acceptable. 2569 return true; 2570 } 2571 2572 2573 2574 /** 2575 * Checks the attributes and object classes contained in this entry 2576 * to determine whether they conform to the server schema 2577 * requirements. 2578 * 2579 * @param ditContentRule The DIT content rule for this entry, if 2580 * any. 2581 * @param structuralPolicy The policy that should be used for 2582 * structural object class compliance. 2583 * @param invalidReason A buffer into which an invalid reason 2584 * may be added. 2585 * 2586 * @return {@code true} if this entry passes all of the checks, or 2587 * {@code false} if there are any failures. 2588 */ 2589 private boolean checkAttributesAndObjectClasses( 2590 DITContentRule ditContentRule, 2591 AcceptRejectWarn structuralPolicy, 2592 MessageBuilder invalidReason) 2593 { 2594 // Make sure that we recognize all of the objectclasses, that all 2595 // auxiliary classes are allowed by the DIT content rule, and that 2596 // all attributes required by the object classes are present. 2597 for (ObjectClass o : objectClasses.keySet()) 2598 { 2599 if (DirectoryServer.getObjectClass(o.getOID()) == null) 2600 { 2601 Message message = ERR_ENTRY_SCHEMA_UNKNOWN_OC.get( 2602 String.valueOf(dn), o 2603 .getNameOrOID()); 2604 invalidReason.append(message); 2605 return false; 2606 } 2607 2608 if ((o.getObjectClassType() == ObjectClassType.AUXILIARY) && 2609 (ditContentRule != null) && 2610 (! ditContentRule.getAuxiliaryClasses().contains(o))) 2611 { 2612 Message message = 2613 ERR_ENTRY_SCHEMA_DISALLOWED_AUXILIARY_CLASS.get( 2614 String.valueOf(dn), 2615 o.getNameOrOID(), 2616 ditContentRule.getName()); 2617 if (structuralPolicy == AcceptRejectWarn.REJECT) 2618 { 2619 invalidReason.append(message); 2620 return false; 2621 } 2622 else if (structuralPolicy == AcceptRejectWarn.WARN) 2623 { 2624 logError(message); 2625 } 2626 } 2627 2628 for (AttributeType t : o.getRequiredAttributes()) 2629 { 2630 if (! (userAttributes.containsKey(t) || 2631 operationalAttributes.containsKey(t) || 2632 t.isObjectClassType())) 2633 { 2634 Message message = 2635 ERR_ENTRY_SCHEMA_MISSING_REQUIRED_ATTR_FOR_OC.get( 2636 String.valueOf(dn), 2637 t.getNameOrOID(), 2638 o.getNameOrOID()); 2639 invalidReason.append(message); 2640 return false; 2641 } 2642 } 2643 } 2644 2645 2646 // Make sure all the user attributes are allowed, have at least 2647 // one value, and if they are single-valued that they have exactly 2648 // one value. 2649 for (AttributeType t : userAttributes.keySet()) 2650 { 2651 boolean found = false; 2652 for (ObjectClass o : objectClasses.keySet()) 2653 { 2654 if (o.isRequiredOrOptional(t)) 2655 { 2656 found = true; 2657 break; 2658 } 2659 } 2660 2661 if ((! found) && (ditContentRule != null)) 2662 { 2663 if (ditContentRule.isRequiredOrOptional(t)) 2664 { 2665 found = true; 2666 } 2667 } 2668 2669 if (! found) 2670 { 2671 Message message = 2672 ERR_ENTRY_SCHEMA_DISALLOWED_USER_ATTR_FOR_OC.get( 2673 String.valueOf(dn), 2674 t.getNameOrOID()); 2675 invalidReason.append(message); 2676 return false; 2677 } 2678 2679 List<Attribute> attrList = userAttributes.get(t); 2680 if (attrList != null) 2681 { 2682 for (Attribute a : attrList) 2683 { 2684 LinkedHashSet<AttributeValue> values = a.getValues(); 2685 if (values.isEmpty()) 2686 { 2687 Message message = ERR_ENTRY_SCHEMA_ATTR_NO_VALUES.get( 2688 String.valueOf(dn), 2689 t.getNameOrOID()); 2690 2691 invalidReason.append(message); 2692 return false; 2693 } 2694 else if (t.isSingleValue() && (values.size() != 1)) 2695 { 2696 Message message = ERR_ENTRY_SCHEMA_ATTR_SINGLE_VALUED.get( 2697 String.valueOf(dn), 2698 t.getNameOrOID()); 2699 2700 invalidReason.append(message); 2701 return false; 2702 } 2703 } 2704 } 2705 } 2706 2707 2708 // Iterate through all of the operational attributes and make sure 2709 // that all of the single-valued attributes only have one value. 2710 for (AttributeType t : operationalAttributes.keySet()) 2711 { 2712 if (t.isSingleValue()) 2713 { 2714 List<Attribute> attrList = operationalAttributes.get(t); 2715 if (attrList != null) 2716 { 2717 for (Attribute a : attrList) 2718 { 2719 if (a.getValues().size() > 1) 2720 { 2721 Message message = 2722 ERR_ENTRY_SCHEMA_ATTR_SINGLE_VALUED.get( 2723 String.valueOf(dn), 2724 t.getNameOrOID()); 2725 2726 invalidReason.append(message); 2727 return false; 2728 } 2729 } 2730 } 2731 } 2732 } 2733 2734 2735 // If we've gotten here, then things are OK. 2736 return true; 2737 } 2738 2739 2740 2741 /** 2742 * Performs any processing needed for name form validation. 2743 * 2744 * @param nameForm The name form to validate against this 2745 * entry. 2746 * @param structuralPolicy The policy that should be used for 2747 * structural object class compliance. 2748 * @param invalidReason A buffer into which an invalid reason 2749 * may be added. 2750 * 2751 * @return {@code true} if this entry passes all of the checks, or 2752 * {@code false} if there are any failures. 2753 */ 2754 private boolean checkNameForm(NameForm nameForm, 2755 AcceptRejectWarn structuralPolicy, 2756 MessageBuilder invalidReason) 2757 { 2758 RDN rdn = dn.getRDN(); 2759 if (rdn != null) 2760 { 2761 // Make sure that all the required attributes are present. 2762 for (AttributeType t : nameForm.getRequiredAttributes()) 2763 { 2764 if (! rdn.hasAttributeType(t)) 2765 { 2766 Message message = 2767 ERR_ENTRY_SCHEMA_RDN_MISSING_REQUIRED_ATTR.get( 2768 String.valueOf(dn), 2769 t.getNameOrOID(), 2770 nameForm.getNameOrOID()); 2771 2772 if (structuralPolicy == AcceptRejectWarn.REJECT) 2773 { 2774 invalidReason.append(message); 2775 return false; 2776 } 2777 else if (structuralPolicy == AcceptRejectWarn.WARN) 2778 { 2779 logError(message); 2780 } 2781 } 2782 } 2783 2784 // Make sure that all attributes in the RDN are allowed. 2785 int numAVAs = rdn.getNumValues(); 2786 for (int i = 0; i < numAVAs; i++) 2787 { 2788 AttributeType t = rdn.getAttributeType(i); 2789 if (! nameForm.isRequiredOrOptional(t)) 2790 { 2791 Message message = 2792 ERR_ENTRY_SCHEMA_RDN_DISALLOWED_ATTR.get( 2793 String.valueOf(dn), 2794 t.getNameOrOID(), 2795 nameForm.getNameOrOID()); 2796 2797 if (structuralPolicy == AcceptRejectWarn.REJECT) 2798 { 2799 invalidReason.append(message); 2800 return false; 2801 } 2802 else if (structuralPolicy == AcceptRejectWarn.WARN) 2803 { 2804 logError(message); 2805 } 2806 } 2807 } 2808 } 2809 2810 // If we've gotten here, then things are OK. 2811 return true; 2812 } 2813 2814 2815 2816 /** 2817 * Performs any processing needed for DIT content rule validation. 2818 * 2819 * @param ditContentRule The DIT content rule to validate 2820 * against this entry. 2821 * @param structuralPolicy The policy that should be used for 2822 * structural object class compliance. 2823 * @param invalidReason A buffer into which an invalid reason 2824 * may be added. 2825 * 2826 * @return {@code true} if this entry passes all of the checks, or 2827 * {@code false} if there are any failures. 2828 */ 2829 private boolean checkDITContentRule(DITContentRule ditContentRule, 2830 AcceptRejectWarn structuralPolicy, 2831 MessageBuilder invalidReason) 2832 { 2833 // Make sure that all of the required attributes are present. 2834 for (AttributeType t : ditContentRule.getRequiredAttributes()) 2835 { 2836 if (! (userAttributes.containsKey(t) || 2837 operationalAttributes.containsKey(t) || 2838 t.isObjectClassType())) 2839 { 2840 Message message = 2841 ERR_ENTRY_SCHEMA_MISSING_REQUIRED_ATTR_FOR_DCR.get( 2842 String.valueOf(dn), 2843 t.getNameOrOID(), 2844 ditContentRule.getName()); 2845 2846 if (structuralPolicy == AcceptRejectWarn.REJECT) 2847 { 2848 invalidReason.append(message); 2849 return false; 2850 } 2851 else if (structuralPolicy == AcceptRejectWarn.WARN) 2852 { 2853 logError(message); 2854 } 2855 } 2856 } 2857 2858 // Make sure that none of the prohibited attributes are present. 2859 for (AttributeType t : ditContentRule.getProhibitedAttributes()) 2860 { 2861 if (userAttributes.containsKey(t) || 2862 operationalAttributes.containsKey(t)) 2863 { 2864 Message message = 2865 ERR_ENTRY_SCHEMA_PROHIBITED_ATTR_FOR_DCR.get( 2866 String.valueOf(dn), 2867 t.getNameOrOID(), 2868 ditContentRule.getName()); 2869 2870 if (structuralPolicy == AcceptRejectWarn.REJECT) 2871 { 2872 invalidReason.append(message); 2873 return false; 2874 } 2875 else if (structuralPolicy == AcceptRejectWarn.WARN) 2876 { 2877 logError(message); 2878 } 2879 } 2880 } 2881 2882 // If we've gotten here, then things are OK. 2883 return true; 2884 } 2885 2886 2887 2888 /** 2889 * Performs any processing needed for DIT structure rule validation. 2890 * 2891 * @param ditStructureRule The DIT structure rule for this 2892 * entry. 2893 * @param structuralClass The structural object class for 2894 * this entry. 2895 * @param parentEntry The parent entry, if available 2896 * and applicable. 2897 * @param parentProvided Indicates whether the parent 2898 * entry was provided. 2899 * @param validateStructureRules Indicates whether to check to see 2900 * if this entry violates a DIT 2901 * structure rule for its parent. 2902 * @param structuralPolicy The policy that should be used 2903 * for structural object class 2904 * compliance. 2905 * @param invalidReason A buffer into which an invalid 2906 * reason may be added. 2907 * 2908 * @return {@code true} if this entry passes all of the checks, or 2909 * {@code false} if there are any failures. 2910 */ 2911 private boolean checkDITStructureRule( 2912 DITStructureRule ditStructureRule, 2913 ObjectClass structuralClass, 2914 Entry parentEntry, boolean parentProvided, 2915 boolean validateStructureRules, 2916 AcceptRejectWarn structuralPolicy, 2917 MessageBuilder invalidReason) 2918 { 2919 // If there is a DIT structure rule for this entry, then make sure 2920 // that the entry is in compliance with it. 2921 if ((ditStructureRule != null) && 2922 ditStructureRule.hasSuperiorRules()) 2923 { 2924 if (parentProvided) 2925 { 2926 if (parentEntry != null) 2927 { 2928 boolean dsrValid = 2929 validateDITStructureRule(ditStructureRule, 2930 structuralClass, parentEntry, 2931 structuralPolicy, 2932 invalidReason); 2933 if (! dsrValid) 2934 { 2935 return false; 2936 } 2937 } 2938 } 2939 else 2940 { 2941 // Get the DN of the parent entry if possible. 2942 DN parentDN = dn.getParentDNInSuffix(); 2943 if (parentDN != null) 2944 { 2945 // Get the parent entry and check its structural class. 2946 Lock lock = null; 2947 for (int i=0; i < 3; i++) 2948 { 2949 lock = LockManager.lockRead(parentDN); 2950 if (lock != null) 2951 { 2952 break; 2953 } 2954 } 2955 2956 if (lock == null) 2957 { 2958 Message message = 2959 ERR_ENTRY_SCHEMA_DSR_COULD_NOT_LOCK_PARENT.get( 2960 String.valueOf(dn), String.valueOf(parentDN)); 2961 2962 if (structuralPolicy == AcceptRejectWarn.REJECT) 2963 { 2964 invalidReason.append(message); 2965 return false; 2966 } 2967 else if (structuralPolicy == AcceptRejectWarn.WARN) 2968 { 2969 logError(message); 2970 } 2971 } 2972 else 2973 { 2974 try 2975 { 2976 parentEntry = DirectoryServer.getEntry(parentDN); 2977 if (parentEntry == null) 2978 { 2979 Message message = 2980 ERR_ENTRY_SCHEMA_DSR_NO_PARENT_ENTRY.get( 2981 String.valueOf(dn), 2982 String.valueOf(parentDN)); 2983 2984 if (structuralPolicy == AcceptRejectWarn.REJECT) 2985 { 2986 invalidReason.append(message); 2987 return false; 2988 } 2989 else if (structuralPolicy == AcceptRejectWarn.WARN) 2990 { 2991 logError(message); 2992 } 2993 } 2994 else 2995 { 2996 boolean dsrValid = 2997 validateDITStructureRule(ditStructureRule, 2998 structuralClass, 2999 parentEntry, 3000 structuralPolicy, 3001 invalidReason); 3002 if (! dsrValid) 3003 { 3004 return false; 3005 } 3006 } 3007 } 3008 catch (Exception e) 3009 { 3010 if (debugEnabled()) 3011 { 3012 TRACER.debugCaught(DebugLogLevel.ERROR, e); 3013 } 3014 3015 Message message = 3016 ERR_ENTRY_SCHEMA_COULD_NOT_CHECK_DSR.get( 3017 String.valueOf(dn), 3018 ditStructureRule.getNameOrRuleID(), 3019 getExceptionMessage(e)); 3020 3021 if (structuralPolicy == AcceptRejectWarn.REJECT) 3022 { 3023 invalidReason.append(message); 3024 return false; 3025 } 3026 else if (structuralPolicy == AcceptRejectWarn.WARN) 3027 { 3028 logError(message); 3029 } 3030 } 3031 finally 3032 { 3033 LockManager.unlock(parentDN, lock); 3034 } 3035 } 3036 } 3037 } 3038 } 3039 else if (validateStructureRules) 3040 { 3041 // There is no DIT structure rule for this entry, but there may 3042 // be one for the parent entry. If there is such a rule for the 3043 // parent entry, then this entry will not be valid. 3044 boolean parentExists = false; 3045 ObjectClass parentStructuralClass = null; 3046 if (parentEntry != null) 3047 { 3048 parentExists = true; 3049 parentStructuralClass = 3050 parentEntry.getStructuralObjectClass(); 3051 } 3052 else if (! parentProvided) 3053 { 3054 DN parentDN = getDN().getParentDNInSuffix(); 3055 if (parentDN != null) 3056 { 3057 // Get the parent entry and check its structural class. 3058 Lock lock = null; 3059 for (int i=0; i < 3; i++) 3060 { 3061 lock = LockManager.lockRead(parentDN); 3062 if (lock != null) 3063 { 3064 break; 3065 } 3066 } 3067 3068 if (lock == null) 3069 { 3070 Message message = 3071 ERR_ENTRY_SCHEMA_DSR_COULD_NOT_LOCK_PARENT.get( 3072 String.valueOf(dn), 3073 String.valueOf(parentDN)); 3074 3075 if (structuralPolicy == AcceptRejectWarn.REJECT) 3076 { 3077 invalidReason.append(message); 3078 return false; 3079 } 3080 else if (structuralPolicy == AcceptRejectWarn.WARN) 3081 { 3082 logError(message); 3083 } 3084 } 3085 else 3086 { 3087 try 3088 { 3089 parentEntry = DirectoryServer.getEntry(parentDN); 3090 if (parentEntry == null) 3091 { 3092 Message message = 3093 ERR_ENTRY_SCHEMA_DSR_NO_PARENT_ENTRY.get( 3094 String.valueOf(dn), 3095 String.valueOf(parentDN)); 3096 3097 if (structuralPolicy == AcceptRejectWarn.REJECT) 3098 { 3099 invalidReason.append(message); 3100 return false; 3101 } 3102 else if (structuralPolicy == AcceptRejectWarn.WARN) 3103 { 3104 logError(message); 3105 } 3106 } 3107 else 3108 { 3109 parentExists = true; 3110 parentStructuralClass = 3111 parentEntry.getStructuralObjectClass(); 3112 } 3113 } 3114 catch (Exception e) 3115 { 3116 if (debugEnabled()) 3117 { 3118 TRACER.debugCaught(DebugLogLevel.ERROR, e); 3119 } 3120 3121 Message message = 3122 ERR_ENTRY_SCHEMA_COULD_NOT_CHECK_PARENT_DSR.get( 3123 String.valueOf(dn), 3124 getExceptionMessage(e)); 3125 3126 if (structuralPolicy == AcceptRejectWarn.REJECT) 3127 { 3128 invalidReason.append(message); 3129 return false; 3130 } 3131 else if (structuralPolicy == AcceptRejectWarn.WARN) 3132 { 3133 logError(message); 3134 } 3135 } 3136 finally 3137 { 3138 LockManager.unlock(parentDN, lock); 3139 } 3140 } 3141 } 3142 } 3143 3144 if (parentExists) 3145 { 3146 if (parentStructuralClass == null) 3147 { 3148 Message message = ERR_ENTRY_SCHEMA_DSR_NO_PARENT_OC.get( 3149 String.valueOf(dn), 3150 String.valueOf(parentEntry.getDN())); 3151 3152 if (structuralPolicy == AcceptRejectWarn.REJECT) 3153 { 3154 invalidReason.append(message); 3155 return false; 3156 } 3157 else if (structuralPolicy == AcceptRejectWarn.WARN) 3158 { 3159 logError(message); 3160 } 3161 } 3162 else 3163 { 3164 NameForm parentNF = 3165 DirectoryServer.getNameForm(parentStructuralClass); 3166 if ((parentNF != null) && (! parentNF.isObsolete())) 3167 { 3168 DITStructureRule parentDSR = 3169 DirectoryServer.getDITStructureRule(parentNF); 3170 if ((parentDSR != null) && (! parentDSR.isObsolete())) 3171 { 3172 Message message = 3173 ERR_ENTRY_SCHEMA_VIOLATES_PARENT_DSR.get( 3174 String.valueOf(dn), 3175 String.valueOf(parentEntry.getDN())); 3176 3177 if (structuralPolicy == AcceptRejectWarn.REJECT) 3178 { 3179 invalidReason.append(message); 3180 return false; 3181 } 3182 else if (structuralPolicy == AcceptRejectWarn.WARN) 3183 { 3184 logError(message); 3185 } 3186 } 3187 } 3188 } 3189 } 3190 } 3191 3192 // If we've gotten here, then things are OK. 3193 return true; 3194 } 3195 3196 3197 3198 /** 3199 * Determines whether this entry is in conformance to the provided 3200 * DIT structure rule. 3201 * 3202 * @param dsr The DIT structure rule to use in the 3203 * determination. 3204 * @param structuralClass The structural objectclass for this 3205 * entry to use in the determination. 3206 * @param parentEntry The reference to the parent entry to 3207 * check. 3208 * @param structuralPolicy The policy that should be used around 3209 * enforcement of DIT structure rules. 3210 * @param invalidReason The buffer to which the invalid reason 3211 * should be appended if a problem is 3212 * found. 3213 * 3214 * @return <CODE>true</CODE> if this entry conforms to the provided 3215 * DIT structure rule, or <CODE>false</CODE> if not. 3216 */ 3217 private boolean validateDITStructureRule(DITStructureRule dsr, 3218 ObjectClass structuralClass, Entry parentEntry, 3219 AcceptRejectWarn structuralPolicy, 3220 MessageBuilder invalidReason) 3221 { 3222 ObjectClass oc = parentEntry.getStructuralObjectClass(); 3223 if (oc == null) 3224 { 3225 Message message = ERR_ENTRY_SCHEMA_DSR_NO_PARENT_OC.get( 3226 String.valueOf(dn), 3227 String.valueOf(parentEntry.getDN())); 3228 3229 if (structuralPolicy == AcceptRejectWarn.REJECT) 3230 { 3231 invalidReason.append(message); 3232 return false; 3233 } 3234 else if (structuralPolicy == AcceptRejectWarn.WARN) 3235 { 3236 logError(message); 3237 } 3238 } 3239 3240 boolean matchFound = false; 3241 for (DITStructureRule dsr2 : dsr.getSuperiorRules()) 3242 { 3243 if (dsr2.getStructuralClass().equals(oc)) 3244 { 3245 matchFound = true; 3246 } 3247 } 3248 3249 if (! matchFound) 3250 { 3251 Message message = 3252 ERR_ENTRY_SCHEMA_DSR_DISALLOWED_SUPERIOR_OC.get( 3253 String.valueOf(dn), 3254 dsr.getNameOrRuleID(), 3255 structuralClass.getNameOrOID(), 3256 oc.getNameOrOID()); 3257 3258 if (structuralPolicy == AcceptRejectWarn.REJECT) 3259 { 3260 invalidReason.append(message); 3261 return false; 3262 } 3263 else if (structuralPolicy == AcceptRejectWarn.WARN) 3264 { 3265 logError(message); 3266 } 3267 } 3268 3269 return true; 3270 } 3271 3272 3273 3274 /** 3275 * Retrieves the attachment for this entry. 3276 * 3277 * @return The attachment for this entry, or <CODE>null</CODE> if 3278 * there is none. 3279 */ 3280 public Object getAttachment() 3281 { 3282 return attachment; 3283 } 3284 3285 3286 3287 /** 3288 * Specifies the attachment for this entry. This will replace any 3289 * existing attachment that might be defined. 3290 * 3291 * @param attachment The attachment for this entry, or 3292 * <CODE>null</CODE> if there should not be an 3293 * attachment. 3294 */ 3295 public void setAttachment(Object attachment) 3296 { 3297 this.attachment = attachment; 3298 } 3299 3300 3301 3302 /** 3303 * Creates a duplicate of this entry that may be altered without 3304 * impacting the information in this entry. 3305 * 3306 * @param processVirtual Indicates whether virtual attribute 3307 * processing should be performed for the 3308 * entry. 3309 * 3310 * @return A duplicate of this entry that may be altered without 3311 * impacting the information in this entry. 3312 */ 3313 public Entry duplicate(boolean processVirtual) 3314 { 3315 HashMap<ObjectClass,String> objectClassesCopy = 3316 new HashMap<ObjectClass,String>(objectClasses); 3317 3318 HashMap<AttributeType,List<Attribute>> userAttrsCopy = 3319 new HashMap<AttributeType,List<Attribute>>( 3320 userAttributes.size()); 3321 deepCopy(userAttributes, userAttrsCopy, false); 3322 3323 HashMap<AttributeType,List<Attribute>> operationalAttrsCopy = 3324 new HashMap<AttributeType,List<Attribute>>( 3325 operationalAttributes.size()); 3326 deepCopy(operationalAttributes, operationalAttrsCopy, false); 3327 3328 for (AttributeType t : suppressedAttributes.keySet()) 3329 { 3330 List<Attribute> attrList = suppressedAttributes.get(t); 3331 if (t.isOperational()) 3332 { 3333 operationalAttributes.put(t, attrList); 3334 } 3335 else 3336 { 3337 userAttributes.put(t, attrList); 3338 } 3339 } 3340 3341 Entry e = new Entry(dn, objectClassesCopy, userAttrsCopy, 3342 operationalAttrsCopy); 3343 if (processVirtual) 3344 { 3345 e.processVirtualAttributes(); 3346 } 3347 return e; 3348 } 3349 3350 3351 3352 /** 3353 * Creates a duplicate of this entry without any operational 3354 * attributes that may be altered without impacting the information 3355 * in this entry. 3356 * 3357 * @param typesOnly Indicates whether to include attribute 3358 * types only without values. 3359 * @param processVirtual Indicates whether virtual attribute 3360 * processing should be performed for the 3361 * entry. 3362 * 3363 * @return A duplicate of this entry that may be altered without 3364 * impacting the information in this entry and that does 3365 * not contain any operational attributes. 3366 */ 3367 public Entry duplicateWithoutOperationalAttributes( 3368 boolean typesOnly, boolean processVirtual) 3369 { 3370 HashMap<ObjectClass,String> objectClassesCopy; 3371 if (typesOnly) 3372 { 3373 objectClassesCopy = new HashMap<ObjectClass,String>(0); 3374 } 3375 else 3376 { 3377 objectClassesCopy = 3378 new HashMap<ObjectClass,String>(objectClasses); 3379 } 3380 3381 HashMap<AttributeType,List<Attribute>> userAttrsCopy = 3382 new HashMap<AttributeType,List<Attribute>>( 3383 userAttributes.size()); 3384 if (typesOnly) 3385 { 3386 // Make sure to include the objectClass attribute here because 3387 // it won't make it in otherwise. 3388 AttributeType ocType = 3389 DirectoryServer.getObjectClassAttributeType(); 3390 ArrayList<Attribute> ocList = new ArrayList<Attribute>(1); 3391 ocList.add(new Attribute(ocType)); 3392 userAttrsCopy.put(ocType, ocList); 3393 } 3394 3395 deepCopy(userAttributes, userAttrsCopy, typesOnly); 3396 3397 HashMap<AttributeType,List<Attribute>> operationalAttrsCopy = 3398 new HashMap<AttributeType,List<Attribute>>(0); 3399 3400 for (AttributeType t : suppressedAttributes.keySet()) 3401 { 3402 List<Attribute> attrList = suppressedAttributes.get(t); 3403 if (! t.isOperational()) 3404 { 3405 userAttributes.put(t, attrList); 3406 } 3407 } 3408 3409 Entry e = new Entry(dn, objectClassesCopy, userAttrsCopy, 3410 operationalAttrsCopy); 3411 3412 if (processVirtual) 3413 { 3414 e.processVirtualAttributes(false); 3415 } 3416 3417 return e; 3418 } 3419 3420 3421 3422 /** 3423 * Performs a deep copy from the source map to the target map. In 3424 * this case, the attributes in the list will be duplicates rather 3425 * than re-using the same reference. Virtual attributes will not be 3426 * included when making the copy. 3427 * 3428 * @param source The source map from which to obtain the 3429 * information. 3430 * @param target The target map into which to place the 3431 * copied information. 3432 * @param omitValues Indicates whether to omit attribute values 3433 * when processing. 3434 */ 3435 private void deepCopy(Map<AttributeType,List<Attribute>> source, 3436 Map<AttributeType,List<Attribute>> target, 3437 boolean omitValues) 3438 { 3439 for (AttributeType t : source.keySet()) 3440 { 3441 List<Attribute> sourceList = source.get(t); 3442 ArrayList<Attribute> targetList = 3443 new ArrayList<Attribute>(sourceList.size()); 3444 3445 for (Attribute a : sourceList) 3446 { 3447 if (a.isVirtual()) 3448 { 3449 continue; 3450 } 3451 3452 targetList.add(a.duplicate(omitValues)); 3453 } 3454 3455 if (! targetList.isEmpty()) 3456 { 3457 target.put(t, targetList); 3458 } 3459 } 3460 } 3461 3462 3463 3464 /** 3465 * Creates a duplicate of this entry without any attribute or 3466 * objectclass information (i.e., it will just contain the DN and 3467 * placeholders for adding attributes) and objectclasses. 3468 * 3469 * @return A duplicate of this entry that may be altered without 3470 * impacting the information in this entry and that does 3471 * not contain attribute or objectclass information. 3472 */ 3473 public Entry duplicateWithoutAttributes() 3474 { 3475 HashMap<ObjectClass,String> objectClassesCopy = 3476 new HashMap<ObjectClass,String>(objectClasses.size()); 3477 3478 HashMap<AttributeType,List<Attribute>> userAttrsCopy = 3479 new HashMap<AttributeType,List<Attribute>>( 3480 userAttributes.size()); 3481 3482 HashMap<AttributeType,List<Attribute>> operationalAttrsCopy = 3483 new HashMap<AttributeType,List<Attribute>>( 3484 operationalAttributes.size()); 3485 3486 return new Entry(dn, objectClassesCopy, userAttrsCopy, 3487 operationalAttrsCopy); 3488 } 3489 3490 3491 3492 /** 3493 * Indicates whether this entry meets the criteria to consider it a 3494 * referral (e.g., it contains the "referral" objectclass and a 3495 * "ref" attribute). 3496 * 3497 * @return <CODE>true</CODE> if this entry meets the criteria to 3498 * consider it a referral, or <CODE>false</CODE> if not. 3499 */ 3500 public boolean isReferral() 3501 { 3502 ObjectClass referralOC = 3503 DirectoryServer.getObjectClass(OC_REFERRAL); 3504 if (referralOC == null) 3505 { 3506 // This should not happen -- The server doesn't have a referral 3507 // objectclass defined. 3508 if (debugEnabled()) 3509 { 3510 TRACER.debugWarning( 3511 "No %s objectclass is defined in the server schema.", 3512 OC_REFERRAL); 3513 } 3514 3515 for (String ocName : objectClasses.values()) 3516 { 3517 if (ocName.equalsIgnoreCase(OC_REFERRAL)) 3518 { 3519 return true; 3520 } 3521 } 3522 3523 return false; 3524 } 3525 3526 if (! objectClasses.containsKey(referralOC)) 3527 { 3528 return false; 3529 } 3530 3531 AttributeType referralType = 3532 DirectoryServer.getAttributeType(ATTR_REFERRAL_URL); 3533 if (referralType == null) 3534 { 3535 // This should not happen -- The server doesn't have a ref 3536 // attribute type defined. 3537 if (debugEnabled()) 3538 { 3539 TRACER.debugWarning( 3540 "No %s attribute type is defined in the server schema.", 3541 ATTR_REFERRAL_URL); 3542 } 3543 return false; 3544 } 3545 3546 return (userAttributes.containsKey(referralType) || 3547 operationalAttributes.containsKey(referralType)); 3548 } 3549 3550 3551 3552 /** 3553 * Retrieves the set of referral URLs that are included in this 3554 * referral entry. This should only be called if 3555 * <CODE>isReferral()</CODE> returns <CODE>true</CODE>. 3556 * 3557 * @return The set of referral URLs that are included in this entry 3558 * if it is a referral, or <CODE>null</CODE> if it is not a 3559 * referral. 3560 */ 3561 public LinkedHashSet<String> getReferralURLs() 3562 { 3563 AttributeType referralType = 3564 DirectoryServer.getAttributeType(ATTR_REFERRAL_URL); 3565 if (referralType == null) 3566 { 3567 // This should not happen -- The server doesn't have a ref 3568 // attribute type defined. 3569 if (debugEnabled()) 3570 { 3571 TRACER.debugWarning( 3572 "No %s attribute type is defined in the server schema.", 3573 ATTR_REFERRAL_URL); 3574 } 3575 return null; 3576 } 3577 3578 List<Attribute> refAttrs = userAttributes.get(referralType); 3579 if (refAttrs == null) 3580 { 3581 refAttrs = operationalAttributes.get(referralType); 3582 if (refAttrs == null) 3583 { 3584 return null; 3585 } 3586 } 3587 3588 LinkedHashSet<String> referralURLs = new LinkedHashSet<String>(); 3589 for (Attribute a : refAttrs) 3590 { 3591 for (AttributeValue v : a.getValues()) 3592 { 3593 referralURLs.add(v.getStringValue()); 3594 } 3595 } 3596 3597 return referralURLs; 3598 } 3599 3600 3601 3602 /** 3603 * Indicates whether this entry meets the criteria to consider it an 3604 * alias (e.g., it contains the "aliasObject" objectclass and a 3605 * "alias" attribute). 3606 * 3607 * @return <CODE>true</CODE> if this entry meets the criteria to 3608 * consider it an alias, or <CODE>false</CODE> if not. 3609 */ 3610 public boolean isAlias() 3611 { 3612 ObjectClass aliasOC = DirectoryServer.getObjectClass(OC_ALIAS); 3613 if (aliasOC == null) 3614 { 3615 // This should not happen -- The server doesn't have an alias 3616 // objectclass defined. 3617 if (debugEnabled()) 3618 { 3619 TRACER.debugWarning( 3620 "No %s objectclass is defined in the server schema.", 3621 OC_ALIAS); 3622 } 3623 3624 for (String ocName : objectClasses.values()) 3625 { 3626 if (ocName.equalsIgnoreCase(OC_ALIAS)) 3627 { 3628 return true; 3629 } 3630 } 3631 3632 return false; 3633 } 3634 3635 if (! objectClasses.containsKey(aliasOC)) 3636 { 3637 return false; 3638 } 3639 3640 AttributeType aliasType = 3641 DirectoryServer.getAttributeType(ATTR_ALIAS_DN); 3642 if (aliasType == null) 3643 { 3644 // This should not happen -- The server doesn't have an 3645 // aliasedObjectName attribute type defined. 3646 if (debugEnabled()) 3647 { 3648 TRACER.debugWarning( 3649 "No %s attribute type is defined in the server schema.", 3650 ATTR_ALIAS_DN); 3651 } 3652 return false; 3653 } 3654 3655 return (userAttributes.containsKey(aliasType) || 3656 operationalAttributes.containsKey(aliasType)); 3657 } 3658 3659 3660 3661 /** 3662 * Retrieves the DN of the entry referenced by this alias entry. 3663 * This should only be called if <CODE>isAlias()</CODE> returns 3664 * <CODE>true</CODE>. 3665 * 3666 * @return The DN of the entry referenced by this alias entry, or 3667 * <CODE>null</CODE> if it is not an alias. 3668 * 3669 * @throws DirectoryException If there is an aliasedObjectName 3670 * attribute but its value cannot be 3671 * parsed as a DN. 3672 */ 3673 public DN getAliasedDN() 3674 throws DirectoryException 3675 { 3676 AttributeType aliasType = 3677 DirectoryServer.getAttributeType(ATTR_REFERRAL_URL); 3678 if (aliasType == null) 3679 { 3680 // This should not happen -- The server doesn't have an 3681 // aliasedObjectName attribute type defined. 3682 if (debugEnabled()) 3683 { 3684 TRACER.debugWarning( 3685 "No %s attribute type is defined in the server schema.", 3686 ATTR_ALIAS_DN); 3687 } 3688 return null; 3689 } 3690 3691 List<Attribute> aliasAttrs = userAttributes.get(aliasType); 3692 if (aliasAttrs == null) 3693 { 3694 aliasAttrs = operationalAttributes.get(aliasType); 3695 if (aliasAttrs == null) 3696 { 3697 return null; 3698 } 3699 } 3700 3701 if (aliasAttrs.isEmpty()) 3702 { 3703 return null; 3704 } 3705 else 3706 { 3707 // There should only be a single alias attribute in an entry, 3708 // and we'll skip the check for others for performance reasons. 3709 // We would just end up taking the first one anyway. The same 3710 // is true with the set of values, since it should be a 3711 // single-valued attribute. 3712 Attribute aliasAttr = aliasAttrs.get(0); 3713 LinkedHashSet<AttributeValue> attrValues = 3714 aliasAttr.getValues(); 3715 if (attrValues.isEmpty()) 3716 { 3717 return null; 3718 } 3719 else 3720 { 3721 return 3722 DN.decode(attrValues.iterator().next().getStringValue()); 3723 } 3724 } 3725 } 3726 3727 3728 3729 /** 3730 * Indicates whether this entry meets the criteria to consider it an 3731 * LDAP subentry (i.e., it contains the "ldapSubentry" objectclass). 3732 * 3733 * @return <CODE>true</CODE> if this entry meets the criteria to 3734 * consider it an LDAP subentry, or <CODE>false</CODE> if 3735 * not. 3736 */ 3737 public boolean isLDAPSubentry() 3738 { 3739 ObjectClass ldapSubentryOC = 3740 DirectoryServer.getObjectClass(OC_LDAP_SUBENTRY_LC); 3741 if (ldapSubentryOC == null) 3742 { 3743 // This should not happen -- The server doesn't have an 3744 // ldapsubentry objectclass defined. 3745 if (debugEnabled()) 3746 { 3747 TRACER.debugWarning( 3748 "No %s objectclass is defined in the server schema.", 3749 OC_LDAP_SUBENTRY); 3750 } 3751 3752 for (String ocName : objectClasses.values()) 3753 { 3754 if (ocName.equalsIgnoreCase(OC_LDAP_SUBENTRY)) 3755 { 3756 return true; 3757 } 3758 } 3759 3760 return false; 3761 } 3762 3763 3764 // Make the determination based on whether this entry has the 3765 // ldapSubentry objectclass. 3766 return objectClasses.containsKey(ldapSubentryOC); 3767 } 3768 3769 3770 3771 /** 3772 * Indicates whether this entry falls within the range of the 3773 * provided search base DN and scope. 3774 * 3775 * @param baseDN The base DN for which to make the determination. 3776 * @param scope The search scope for which to make the 3777 * determination. 3778 * 3779 * @return <CODE>true</CODE> if this entry is within the given 3780 * base and scope, or <CODE>false</CODE> if it is not. 3781 */ 3782 public boolean matchesBaseAndScope(DN baseDN, SearchScope scope) 3783 { 3784 return dn.matchesBaseAndScope(baseDN, scope); 3785 } 3786 3787 3788 3789 /** 3790 * Performs any necessary virtual attribute processing for this 3791 * entry. This should only be called at the time the entry is 3792 * decoded or created within the backend. 3793 */ 3794 public void processVirtualAttributes() 3795 { 3796 processVirtualAttributes(true); 3797 } 3798 3799 3800 3801 /** 3802 * Performs any necessary virtual attribute processing for this 3803 * entry. This should only be called at the time the entry is 3804 * decoded or created within the backend. 3805 * 3806 * @param includeOperational Indicates whether to include 3807 * operational attributes. 3808 */ 3809 public void processVirtualAttributes(boolean includeOperational) 3810 { 3811 for (VirtualAttributeRule rule : 3812 DirectoryServer.getVirtualAttributes(this)) 3813 { 3814 AttributeType attributeType = rule.getAttributeType(); 3815 if (attributeType.isOperational() && (! includeOperational)) 3816 { 3817 continue; 3818 } 3819 3820 List<Attribute> attrList = userAttributes.get(attributeType); 3821 if ((attrList == null) || attrList.isEmpty()) 3822 { 3823 attrList = operationalAttributes.get(attributeType); 3824 if ((attrList == null) || attrList.isEmpty()) 3825 { 3826 // There aren't any conflicts, so we can just add the 3827 // attribute to the entry. 3828 attrList = new LinkedList<Attribute>(); 3829 attrList.add(new VirtualAttribute(attributeType, this, 3830 rule)); 3831 if (attributeType.isOperational()) 3832 { 3833 operationalAttributes.put(attributeType, attrList); 3834 } 3835 else 3836 { 3837 userAttributes.put(attributeType, attrList); 3838 } 3839 } 3840 else 3841 { 3842 // There is a conflict with an existing operational 3843 // attribute. 3844 if (attrList.get(0).isVirtual()) 3845 { 3846 // The existing attribute is already virtual, so we've got 3847 // a different conflict, but we'll let the first win. 3848 // FIXME -- Should we handle this differently? 3849 continue; 3850 } 3851 3852 // The conflict is with a real attribute. See what the 3853 // conflict behavior is and figure out how to handle it. 3854 switch (rule.getConflictBehavior()) 3855 { 3856 case REAL_OVERRIDES_VIRTUAL: 3857 // We don't need to update the entry because the real 3858 // attribute will take precedence. 3859 break; 3860 3861 case VIRTUAL_OVERRIDES_REAL: 3862 // We need to move the real attribute to the suppressed 3863 // list and replace it with the virtual attribute. 3864 suppressedAttributes.put(attributeType, attrList); 3865 attrList = new LinkedList<Attribute>(); 3866 attrList.add(new VirtualAttribute(attributeType, this, 3867 rule)); 3868 operationalAttributes.put(attributeType, attrList); 3869 break; 3870 3871 case MERGE_REAL_AND_VIRTUAL: 3872 // We need to add the virtual attribute to the list and 3873 // keep the existing real attribute(s). 3874 attrList.add(new VirtualAttribute(attributeType, this, 3875 rule)); 3876 break; 3877 } 3878 } 3879 } 3880 else 3881 { 3882 // There is a conflict with an existing user attribute. 3883 if (attrList.get(0).isVirtual()) 3884 { 3885 // The existing attribute is already virtual, so we've got 3886 // a different conflict, but we'll let the first win. 3887 // FIXME -- Should we handle this differently? 3888 continue; 3889 } 3890 3891 // The conflict is with a real attribute. See what the 3892 // conflict behavior is and figure out how to handle it. 3893 switch (rule.getConflictBehavior()) 3894 { 3895 case REAL_OVERRIDES_VIRTUAL: 3896 // We don't need to update the entry because the real 3897 // attribute will take precedence. 3898 break; 3899 3900 case VIRTUAL_OVERRIDES_REAL: 3901 // We need to move the real attribute to the suppressed 3902 // list and replace it with the virtual attribute. 3903 suppressedAttributes.put(attributeType, attrList); 3904 attrList = new LinkedList<Attribute>(); 3905 attrList.add(new VirtualAttribute(attributeType, this, 3906 rule)); 3907 userAttributes.put(attributeType, attrList); 3908 break; 3909 3910 case MERGE_REAL_AND_VIRTUAL: 3911 // We need to add the virtual attribute to the list and 3912 // keep the existing real attribute(s). 3913 attrList.add(new VirtualAttribute(attributeType, this, 3914 rule)); 3915 break; 3916 } 3917 } 3918 } 3919 3920 virtualAttributeProcessingPerformed = true; 3921 } 3922 3923 3924 3925 /** 3926 * Indicates whether virtual attribute processing has been performed 3927 * for this entry. 3928 * 3929 * @return {@code true} if virtual attribute processing has been 3930 * performed for this entry, or {@code false} if not. 3931 */ 3932 public boolean virtualAttributeProcessingPerformed() 3933 { 3934 return virtualAttributeProcessingPerformed; 3935 } 3936 3937 3938 3939 /** 3940 * Strips out all real attributes from this entry so that it only 3941 * contains virtual attributes. 3942 */ 3943 public void stripRealAttributes() 3944 { 3945 // The objectClass attribute will always be a real attribute. 3946 objectClasses.clear(); 3947 3948 Iterator<Map.Entry<AttributeType,List<Attribute>>> 3949 attrListIterator = userAttributes.entrySet().iterator(); 3950 while (attrListIterator.hasNext()) 3951 { 3952 Map.Entry<AttributeType,List<Attribute>> mapEntry = 3953 attrListIterator.next(); 3954 Iterator<Attribute> attrIterator = 3955 mapEntry.getValue().iterator(); 3956 while (attrIterator.hasNext()) 3957 { 3958 Attribute a = attrIterator.next(); 3959 if (! a.isVirtual()) 3960 { 3961 attrIterator.remove(); 3962 } 3963 } 3964 3965 if (mapEntry.getValue().isEmpty()) 3966 { 3967 attrListIterator.remove(); 3968 } 3969 } 3970 3971 attrListIterator = operationalAttributes.entrySet().iterator(); 3972 while (attrListIterator.hasNext()) 3973 { 3974 Map.Entry<AttributeType,List<Attribute>> mapEntry = 3975 attrListIterator.next(); 3976 Iterator<Attribute> attrIterator = 3977 mapEntry.getValue().iterator(); 3978 while (attrIterator.hasNext()) 3979 { 3980 Attribute a = attrIterator.next(); 3981 if (! a.isVirtual()) 3982 { 3983 attrIterator.remove(); 3984 } 3985 } 3986 3987 if (mapEntry.getValue().isEmpty()) 3988 { 3989 attrListIterator.remove(); 3990 } 3991 } 3992 } 3993 3994 3995 3996 /** 3997 * Strips out all virtual attributes from this entry so that it only 3998 * contains real attributes. 3999 */ 4000 public void stripVirtualAttributes() 4001 { 4002 Iterator<Map.Entry<AttributeType,List<Attribute>>> 4003 attrListIterator = userAttributes.entrySet().iterator(); 4004 while (attrListIterator.hasNext()) 4005 { 4006 Map.Entry<AttributeType,List<Attribute>> mapEntry = 4007 attrListIterator.next(); 4008 Iterator<Attribute> attrIterator = 4009 mapEntry.getValue().iterator(); 4010 while (attrIterator.hasNext()) 4011 { 4012 Attribute a = attrIterator.next(); 4013 if (a.isVirtual()) 4014 { 4015 attrIterator.remove(); 4016 } 4017 } 4018 4019 if (mapEntry.getValue().isEmpty()) 4020 { 4021 attrListIterator.remove(); 4022 } 4023 } 4024 4025 attrListIterator = operationalAttributes.entrySet().iterator(); 4026 while (attrListIterator.hasNext()) 4027 { 4028 Map.Entry<AttributeType,List<Attribute>> mapEntry = 4029 attrListIterator.next(); 4030 Iterator<Attribute> attrIterator = 4031 mapEntry.getValue().iterator(); 4032 while (attrIterator.hasNext()) 4033 { 4034 Attribute a = attrIterator.next(); 4035 if (a.isVirtual()) 4036 { 4037 attrIterator.remove(); 4038 } 4039 } 4040 4041 if (mapEntry.getValue().isEmpty()) 4042 { 4043 attrListIterator.remove(); 4044 } 4045 } 4046 } 4047 4048 4049 4050 /** 4051 * Encodes this entry into a form that is suitable for long-term 4052 * persistent storage. The encoding will have a version number so 4053 * that if the way we store entries changes in the future we will 4054 * still be able to read entries encoded in an older format. 4055 * 4056 * @param config The configuration that may be used to control how 4057 * the entry is encoded. 4058 * 4059 * @return The entry encoded in a form that is suitable for 4060 * long-term persistent storage. 4061 * 4062 * @throws DirectoryException If a problem occurs while attempting 4063 * to encode the entry. 4064 */ 4065 public byte[] encode(EntryEncodeConfig config) 4066 throws DirectoryException 4067 { 4068 return encodeV2(config); 4069 } 4070 4071 4072 4073 /** 4074 * Encodes this entry using the V1 encoding. 4075 * 4076 * @return The entry encoded in the V1 encoding. 4077 */ 4078 public byte[] encodeV1() 4079 { 4080 // The version number will be one byte. We'll add that later. 4081 4082 4083 // The DN will be encoded as a one-to-five byte length followed 4084 // byte the UTF-8 byte representation. 4085 byte[] dnBytes = getBytes(dn.toString()); 4086 byte[] dnLength = ASN1Element.encodeLength(dnBytes.length); 4087 int totalBytes = 1 + dnBytes.length + dnLength.length; 4088 4089 4090 // The object classes will be encoded as one-to-five byte length 4091 // followed by a zero-delimited UTF-8 byte representation of the 4092 // names (e.g., top\0person\0organizationalPerson\0inetOrgPerson). 4093 int i=0; 4094 int totalOCBytes = objectClasses.size() - 1; 4095 byte[][] ocBytes = new byte[objectClasses.size()][]; 4096 for (String ocName : objectClasses.values()) 4097 { 4098 ocBytes[i] = getBytes(ocName); 4099 totalOCBytes += ocBytes[i++].length; 4100 } 4101 byte[] ocLength = ASN1Element.encodeLength(totalOCBytes); 4102 totalBytes += totalOCBytes + ocLength.length; 4103 4104 4105 // The user attributes will be encoded as a one-to-five byte 4106 // number of attributes followed by a sequence of: 4107 // - A UTF-8 byte representation of the attribute name. 4108 // - A zero delimiter 4109 // - A one-to-five byte number of values for the attribute 4110 // - A sequence of: 4111 // - A one-to-five byte length for the value 4112 // - A UTF-8 byte representation for the value 4113 i=0; 4114 int numUserAttributes = 0; 4115 int totalUserAttrBytes = 0; 4116 LinkedList<byte[]> userAttrBytes = new LinkedList<byte[]>(); 4117 for (List<Attribute> attrList : userAttributes.values()) 4118 { 4119 for (Attribute a : attrList) 4120 { 4121 if (a.isVirtual() || (! a.hasValue())) 4122 { 4123 continue; 4124 } 4125 4126 numUserAttributes++; 4127 4128 byte[] nameBytes = getBytes(a.getNameWithOptions()); 4129 4130 int numValues = 0; 4131 int totalValueBytes = 0; 4132 LinkedList<byte[]> valueBytes = new LinkedList<byte[]>(); 4133 for (AttributeValue v : a.getValues()) 4134 { 4135 numValues++; 4136 byte[] vBytes = v.getValueBytes(); 4137 byte[] vLength = ASN1Element.encodeLength(vBytes.length); 4138 valueBytes.add(vLength); 4139 valueBytes.add(vBytes); 4140 totalValueBytes += vLength.length + vBytes.length; 4141 } 4142 byte[] numValuesBytes = ASN1Element.encodeLength(numValues); 4143 4144 byte[] attrBytes = new byte[nameBytes.length + 4145 numValuesBytes.length + 4146 totalValueBytes + 1]; 4147 System.arraycopy(nameBytes, 0, attrBytes, 0, 4148 nameBytes.length); 4149 4150 int pos = nameBytes.length+1; 4151 System.arraycopy(numValuesBytes, 0, attrBytes, pos, 4152 numValuesBytes.length); 4153 pos += numValuesBytes.length; 4154 for (byte[] b : valueBytes) 4155 { 4156 System.arraycopy(b, 0, attrBytes, pos, b.length); 4157 pos += b.length; 4158 } 4159 4160 userAttrBytes.add(attrBytes); 4161 totalUserAttrBytes += attrBytes.length; 4162 } 4163 } 4164 byte[] userAttrCount = 4165 ASN1OctetString.encodeLength(numUserAttributes); 4166 totalBytes += totalUserAttrBytes + userAttrCount.length; 4167 4168 4169 // The operational attributes will be encoded in the same way as 4170 // the user attributes. 4171 i=0; 4172 int numOperationalAttributes = 0; 4173 int totalOperationalAttrBytes = 0; 4174 LinkedList<byte[]> operationalAttrBytes = 4175 new LinkedList<byte[]>(); 4176 for (List<Attribute> attrList : operationalAttributes.values()) 4177 { 4178 for (Attribute a : attrList) 4179 { 4180 if (a.isVirtual() || (! a.hasValue())) 4181 { 4182 continue; 4183 } 4184 4185 numOperationalAttributes++; 4186 4187 byte[] nameBytes = getBytes(a.getNameWithOptions()); 4188 4189 int numValues = 0; 4190 int totalValueBytes = 0; 4191 LinkedList<byte[]> valueBytes = new LinkedList<byte[]>(); 4192 for (AttributeValue v : a.getValues()) 4193 { 4194 numValues++; 4195 byte[] vBytes = v.getValueBytes(); 4196 byte[] vLength = ASN1Element.encodeLength(vBytes.length); 4197 valueBytes.add(vLength); 4198 valueBytes.add(vBytes); 4199 totalValueBytes += vLength.length + vBytes.length; 4200 } 4201 byte[] numValuesBytes = ASN1Element.encodeLength(numValues); 4202 4203 byte[] attrBytes = new byte[nameBytes.length + 4204 numValuesBytes.length + 4205 totalValueBytes + 1]; 4206 System.arraycopy(nameBytes, 0, attrBytes, 0, 4207 nameBytes.length); 4208 4209 int pos = nameBytes.length+1; 4210 System.arraycopy(numValuesBytes, 0, attrBytes, pos, 4211 numValuesBytes.length); 4212 pos += numValuesBytes.length; 4213 for (byte[] b : valueBytes) 4214 { 4215 System.arraycopy(b, 0, attrBytes, pos, b.length); 4216 pos += b.length; 4217 } 4218 4219 operationalAttrBytes.add(attrBytes); 4220 totalOperationalAttrBytes += attrBytes.length; 4221 } 4222 } 4223 byte[] operationalAttrCount = 4224 ASN1OctetString.encodeLength(numOperationalAttributes); 4225 totalBytes += totalOperationalAttrBytes + 4226 operationalAttrCount.length; 4227 4228 4229 // Now we've got all the data that we need. Create a big byte 4230 // array to hold it all and pack it in. 4231 byte[] entryBytes = new byte[totalBytes]; 4232 4233 4234 // Add the entry version number as the first byte. 4235 entryBytes[0] = 0x01; 4236 4237 4238 // Next, add the DN length and value. 4239 System.arraycopy(dnLength, 0, entryBytes, 1, dnLength.length); 4240 int pos = 1 + dnLength.length; 4241 System.arraycopy(dnBytes, 0, entryBytes, pos, dnBytes.length); 4242 pos += dnBytes.length; 4243 4244 4245 // Next, add the object classes length and values. 4246 System.arraycopy(ocLength, 0, entryBytes, pos, ocLength.length); 4247 pos += ocLength.length; 4248 for (byte[] b : ocBytes) 4249 { 4250 System.arraycopy(b, 0, entryBytes, pos, b.length); 4251 pos += b.length + 1; 4252 } 4253 4254 // We need to back up one because there's no zero-teriminator 4255 // after the last object class name. 4256 pos--; 4257 4258 4259 // Next, add the user attribute count and the user attribute 4260 // data. 4261 System.arraycopy(userAttrCount, 0, entryBytes, pos, 4262 userAttrCount.length); 4263 pos += userAttrCount.length; 4264 for (byte[] b : userAttrBytes) 4265 { 4266 System.arraycopy(b, 0, entryBytes, pos, b.length); 4267 pos += b.length; 4268 } 4269 4270 4271 // Finally, add the operational attribute count and the 4272 // operational attribute data. 4273 System.arraycopy(operationalAttrCount, 0, entryBytes, pos, 4274 operationalAttrCount.length); 4275 pos += operationalAttrCount.length; 4276 for (byte[] b : operationalAttrBytes) 4277 { 4278 System.arraycopy(b, 0, entryBytes, pos, b.length); 4279 pos += b.length; 4280 } 4281 4282 return entryBytes; 4283 } 4284 4285 4286 4287 /** 4288 * Encodes this entry using the V2 encoding. 4289 * 4290 * @param config The configuration that should be used to encode 4291 * the entry. 4292 * 4293 * @return The entry encoded in the V2 encoding. 4294 * 4295 * @throws DirectoryException If a problem occurs while attempting 4296 * to encode the entry. 4297 */ 4298 public byte[] encodeV2(EntryEncodeConfig config) 4299 throws DirectoryException 4300 { 4301 // The version number will be one byte. We'll add that later. 4302 4303 4304 // Get the encoded respresentation of the config. 4305 byte[] configBytes = config.encode(); 4306 byte[] configLength = 4307 ASN1Element.encodeLength(configBytes.length); 4308 int totalBytes = 1 + configBytes.length + configLength.length; 4309 4310 4311 // If we should include the DN, then it will be encoded as a 4312 // one-to-five byte length followed by the UTF-8 byte 4313 // representation. 4314 byte[] dnBytes = null; 4315 byte[] dnLength = null; 4316 if (! config.excludeDN()) 4317 { 4318 dnBytes = getBytes(dn.toString()); 4319 dnLength = ASN1Element.encodeLength(dnBytes.length); 4320 totalBytes += dnBytes.length + dnLength.length; 4321 } 4322 4323 4324 // Encode the object classes in the appropriate manner. 4325 byte[] ocLength; 4326 LinkedList<byte[]> ocBytes = new LinkedList<byte[]>(); 4327 if (config.compressObjectClassSets()) 4328 { 4329 byte[] b = config.getCompressedSchema(). 4330 encodeObjectClasses(objectClasses); 4331 ocBytes.add(b); 4332 ocLength = ASN1Element.encodeLength(b.length); 4333 totalBytes += ocLength.length + b.length; 4334 } 4335 else 4336 { 4337 int totalOCBytes = objectClasses.size() - 1; 4338 for (String ocName : objectClasses.values()) 4339 { 4340 byte[] b = getBytes(ocName); 4341 ocBytes.add(b); 4342 totalOCBytes += b.length; 4343 } 4344 ocLength = ASN1Element.encodeLength(totalOCBytes); 4345 totalBytes += totalOCBytes + ocLength.length; 4346 } 4347 4348 4349 // Encode the user attributes in the appropriate manner. 4350 int numUserAttributes = 0; 4351 int totalUserAttrBytes = 0; 4352 LinkedList<byte[]> userAttrBytes = new LinkedList<byte[]>(); 4353 if (config.compressAttributeDescriptions()) 4354 { 4355 for (List<Attribute> attrList : userAttributes.values()) 4356 { 4357 for (Attribute a : attrList) 4358 { 4359 if (a.isVirtual() || (! a.hasValue())) 4360 { 4361 continue; 4362 } 4363 4364 numUserAttributes++; 4365 4366 byte[] attrBytes = 4367 config.getCompressedSchema().encodeAttribute(a); 4368 byte[] lengthBytes = 4369 ASN1Element.encodeLength(attrBytes.length); 4370 userAttrBytes.add(lengthBytes); 4371 userAttrBytes.add(attrBytes); 4372 totalUserAttrBytes += lengthBytes.length + attrBytes.length; 4373 } 4374 } 4375 } 4376 else 4377 { 4378 // The user attributes will be encoded as a one-to-five byte 4379 // number of attributes followed by a sequence of: 4380 // - A UTF-8 byte representation of the attribute name. 4381 // - A zero delimiter 4382 // - A one-to-five byte number of values for the attribute 4383 // - A sequence of: 4384 // - A one-to-five byte length for the value 4385 // - A UTF-8 byte representation for the value 4386 for (List<Attribute> attrList : userAttributes.values()) 4387 { 4388 for (Attribute a : attrList) 4389 { 4390 if (a.isVirtual() || (! a.hasValue())) 4391 { 4392 continue; 4393 } 4394 4395 numUserAttributes++; 4396 4397 byte[] nameBytes = getBytes(a.getNameWithOptions()); 4398 4399 int numValues = 0; 4400 int totalValueBytes = 0; 4401 LinkedList<byte[]> valueBytes = new LinkedList<byte[]>(); 4402 for (AttributeValue v : a.getValues()) 4403 { 4404 numValues++; 4405 byte[] vBytes = v.getValueBytes(); 4406 byte[] vLength = ASN1Element.encodeLength(vBytes.length); 4407 valueBytes.add(vLength); 4408 valueBytes.add(vBytes); 4409 totalValueBytes += vLength.length + vBytes.length; 4410 } 4411 byte[] numValuesBytes = ASN1Element.encodeLength(numValues); 4412 4413 byte[] attrBytes = new byte[nameBytes.length + 4414 numValuesBytes.length + 4415 totalValueBytes + 1]; 4416 System.arraycopy(nameBytes, 0, attrBytes, 0, 4417 nameBytes.length); 4418 4419 int pos = nameBytes.length+1; 4420 System.arraycopy(numValuesBytes, 0, attrBytes, pos, 4421 numValuesBytes.length); 4422 pos += numValuesBytes.length; 4423 for (byte[] b : valueBytes) 4424 { 4425 System.arraycopy(b, 0, attrBytes, pos, b.length); 4426 pos += b.length; 4427 } 4428 4429 userAttrBytes.add(attrBytes); 4430 totalUserAttrBytes += attrBytes.length; 4431 } 4432 } 4433 } 4434 byte[] userAttrCount = 4435 ASN1OctetString.encodeLength(numUserAttributes); 4436 totalBytes += totalUserAttrBytes + userAttrCount.length; 4437 4438 4439 // Encode the operational attributes in the appropriate manner. 4440 int numOperationalAttributes = 0; 4441 int totalOperationalAttrBytes = 0; 4442 LinkedList<byte[]> operationalAttrBytes = 4443 new LinkedList<byte[]>(); 4444 if (config.compressAttributeDescriptions()) 4445 { 4446 for (List<Attribute> attrList : operationalAttributes.values()) 4447 { 4448 for (Attribute a : attrList) 4449 { 4450 if (a.isVirtual() || (! a.hasValue())) 4451 { 4452 continue; 4453 } 4454 4455 numOperationalAttributes++; 4456 4457 byte[] attrBytes = 4458 config.getCompressedSchema().encodeAttribute(a); 4459 byte[] lengthBytes = 4460 ASN1Element.encodeLength(attrBytes.length); 4461 operationalAttrBytes.add(lengthBytes); 4462 operationalAttrBytes.add(attrBytes); 4463 totalOperationalAttrBytes += 4464 lengthBytes.length + attrBytes.length; 4465 } 4466 } 4467 } 4468 else 4469 { 4470 // Encode the operational attributes in the same way as the user 4471 // attributes. 4472 for (List<Attribute> attrList : operationalAttributes.values()) 4473 { 4474 for (Attribute a : attrList) 4475 { 4476 if (a.isVirtual() || (! a.hasValue())) 4477 { 4478 continue; 4479 } 4480 4481 numOperationalAttributes++; 4482 4483 byte[] nameBytes = getBytes(a.getNameWithOptions()); 4484 4485 int numValues = 0; 4486 int totalValueBytes = 0; 4487 LinkedList<byte[]> valueBytes = new LinkedList<byte[]>(); 4488 for (AttributeValue v : a.getValues()) 4489 { 4490 numValues++; 4491 byte[] vBytes = v.getValueBytes(); 4492 byte[] vLength = ASN1Element.encodeLength(vBytes.length); 4493 valueBytes.add(vLength); 4494 valueBytes.add(vBytes); 4495 totalValueBytes += vLength.length + vBytes.length; 4496 } 4497 byte[] numValuesBytes = ASN1Element.encodeLength(numValues); 4498 4499 byte[] attrBytes = new byte[nameBytes.length + 4500 numValuesBytes.length + 4501 totalValueBytes + 1]; 4502 System.arraycopy(nameBytes, 0, attrBytes, 0, 4503 nameBytes.length); 4504 4505 int pos = nameBytes.length+1; 4506 System.arraycopy(numValuesBytes, 0, attrBytes, pos, 4507 numValuesBytes.length); 4508 pos += numValuesBytes.length; 4509 for (byte[] b : valueBytes) 4510 { 4511 System.arraycopy(b, 0, attrBytes, pos, b.length); 4512 pos += b.length; 4513 } 4514 4515 operationalAttrBytes.add(attrBytes); 4516 totalOperationalAttrBytes += attrBytes.length; 4517 } 4518 } 4519 } 4520 byte[] operationalAttrCount = 4521 ASN1OctetString.encodeLength(numOperationalAttributes); 4522 totalBytes += totalOperationalAttrBytes + 4523 operationalAttrCount.length; 4524 4525 4526 // Now we've got all the data that we need. Create a big byte 4527 // array to hold it all and pack it in. 4528 byte[] entryBytes = new byte[totalBytes]; 4529 4530 4531 // Add the entry version number as the first byte. 4532 entryBytes[0] = 0x02; 4533 4534 4535 // Next, add the encoded config. 4536 System.arraycopy(configLength, 0, entryBytes, 1, 4537 configLength.length); 4538 int pos = 1 + configLength.length; 4539 System.arraycopy(configBytes, 0, entryBytes, pos, 4540 configBytes.length); 4541 pos += configBytes.length; 4542 4543 4544 // Next, add the DN length and value. 4545 if (! config.excludeDN()) 4546 { 4547 System.arraycopy(dnLength, 0, entryBytes, pos, dnLength.length); 4548 pos += dnLength.length; 4549 System.arraycopy(dnBytes, 0, entryBytes, pos, dnBytes.length); 4550 pos += dnBytes.length; 4551 } 4552 4553 4554 // Next, add the object classes length and values. 4555 System.arraycopy(ocLength, 0, entryBytes, pos, ocLength.length); 4556 pos += ocLength.length; 4557 if (config.compressObjectClassSets()) 4558 { 4559 for (byte[] b : ocBytes) 4560 { 4561 System.arraycopy(b, 0, entryBytes, pos, b.length); 4562 pos += b.length; 4563 } 4564 } 4565 else 4566 { 4567 for (byte[] b : ocBytes) 4568 { 4569 System.arraycopy(b, 0, entryBytes, pos, b.length); 4570 pos += b.length + 1; 4571 } 4572 4573 // We need to back up one because there's no zero-teriminator 4574 // after the last object class name. 4575 pos--; 4576 } 4577 4578 4579 // Next, add the user attribute count and the user attribute 4580 // data. 4581 System.arraycopy(userAttrCount, 0, entryBytes, pos, 4582 userAttrCount.length); 4583 pos += userAttrCount.length; 4584 for (byte[] b : userAttrBytes) 4585 { 4586 System.arraycopy(b, 0, entryBytes, pos, b.length); 4587 pos += b.length; 4588 } 4589 4590 4591 // Finally, add the operational attribute count and the 4592 // operational attribute data. 4593 System.arraycopy(operationalAttrCount, 0, entryBytes, pos, 4594 operationalAttrCount.length); 4595 pos += operationalAttrCount.length; 4596 for (byte[] b : operationalAttrBytes) 4597 { 4598 System.arraycopy(b, 0, entryBytes, pos, b.length); 4599 pos += b.length; 4600 } 4601 4602 return entryBytes; 4603 } 4604 4605 4606 4607 /** 4608 * Decodes the provided byte array as an entry. 4609 * 4610 * @param entryBytes The byte array containing the data to be 4611 * decoded. 4612 * 4613 * @return The decoded entry. 4614 * 4615 * @throws DirectoryException If the provided byte array cannot be 4616 * decoded as an entry. 4617 */ 4618 public static Entry decode(byte[] entryBytes) 4619 throws DirectoryException 4620 { 4621 return decode(entryBytes, 4622 DirectoryServer.getDefaultCompressedSchema()); 4623 } 4624 4625 4626 4627 /** 4628 * Decodes the provided byte array as an entry. 4629 * 4630 * @param entryBytes The byte array containing the data to 4631 * be decoded. 4632 * @param compressedSchema The compressed schema manager to use 4633 * when decoding tokenized schema 4634 * elements. 4635 * 4636 * @return The decoded entry. 4637 * 4638 * @throws DirectoryException If the provided byte array cannot be 4639 * decoded as an entry. 4640 */ 4641 public static Entry decode(byte[] entryBytes, 4642 CompressedSchema compressedSchema) 4643 throws DirectoryException 4644 { 4645 switch(entryBytes[0]) 4646 { 4647 case 0x01: 4648 return decodeV1(entryBytes); 4649 case 0x02: 4650 return decodeV2(entryBytes, compressedSchema); 4651 default: 4652 Message message = ERR_ENTRY_DECODE_UNRECOGNIZED_VERSION.get( 4653 byteToHex(entryBytes[0])); 4654 throw new DirectoryException( 4655 DirectoryServer.getServerErrorResultCode(), 4656 message); 4657 } 4658 } 4659 4660 4661 4662 /** 4663 * Decodes the provided byte array as an entry using the V1 4664 * encoding. 4665 * 4666 * @param entryBytes The byte array containing the data to be 4667 * decoded. 4668 * 4669 * @return The decoded entry. 4670 * 4671 * @throws DirectoryException If the provided byte array cannot be 4672 * decoded as an entry. 4673 */ 4674 public static Entry decodeV1(byte[] entryBytes) 4675 throws DirectoryException 4676 { 4677 try 4678 { 4679 // The first byte must be the entry version. If it's not one 4680 // we recognize, then that's an error. 4681 if (entryBytes[0] != 0x01) 4682 { 4683 Message message = ERR_ENTRY_DECODE_UNRECOGNIZED_VERSION.get( 4684 byteToHex(entryBytes[0])); 4685 throw new DirectoryException( 4686 DirectoryServer.getServerErrorResultCode(), 4687 message); 4688 } 4689 4690 4691 // Next is the length of the DN. It may be a single byte or 4692 // multiple bytes. 4693 int pos = 1; 4694 int dnLength = entryBytes[pos] & 0x7F; 4695 if (entryBytes[pos++] != dnLength) 4696 { 4697 int numLengthBytes = dnLength; 4698 dnLength = 0; 4699 for (int i=0; i < numLengthBytes; i++, pos++) 4700 { 4701 dnLength = (dnLength << 8) | (entryBytes[pos] & 0xFF); 4702 } 4703 } 4704 4705 4706 // Next is the DN itself. 4707 byte[] dnBytes = new byte[dnLength]; 4708 System.arraycopy(entryBytes, pos, dnBytes, 0, dnLength); 4709 pos += dnLength; 4710 DN dn = DN.decode(new ASN1OctetString(dnBytes)); 4711 4712 4713 // Next is the length of the object classes. It may be a single 4714 // byte or multiple bytes. 4715 int ocLength = entryBytes[pos] & 0x7F; 4716 if (entryBytes[pos++] != ocLength) 4717 { 4718 int numLengthBytes = ocLength; 4719 ocLength = 0; 4720 for (int i=0; i < numLengthBytes; i++, pos++) 4721 { 4722 ocLength = (ocLength << 8) | (entryBytes[pos] & 0xFF); 4723 } 4724 } 4725 4726 4727 // Next is the encoded set of object classes. It will be a 4728 // single string with the object class names separated by zeros. 4729 LinkedHashMap<ObjectClass,String> objectClasses = 4730 new LinkedHashMap<ObjectClass,String>(); 4731 int startPos = pos; 4732 for (int i=0; i < ocLength; i++,pos++) 4733 { 4734 if (entryBytes[pos] == 0x00) 4735 { 4736 String name = new String(entryBytes, startPos, pos-startPos, 4737 "UTF-8"); 4738 String lowerName = toLowerCase(name); 4739 ObjectClass oc = 4740 DirectoryServer.getObjectClass(lowerName, true); 4741 objectClasses.put(oc, name); 4742 startPos = pos+1; 4743 } 4744 } 4745 String name = new String(entryBytes, startPos, pos-startPos, 4746 "UTF-8"); 4747 String lowerName = toLowerCase(name); 4748 ObjectClass oc = 4749 DirectoryServer.getObjectClass(lowerName, true); 4750 objectClasses.put(oc, name); 4751 4752 4753 // Next is the total number of user attributes. It may be a 4754 // single byte or multiple bytes. 4755 int numUserAttrs = entryBytes[pos] & 0x7F; 4756 if (entryBytes[pos++] != numUserAttrs) 4757 { 4758 int numLengthBytes = numUserAttrs; 4759 numUserAttrs = 0; 4760 for (int i=0; i < numLengthBytes; i++, pos++) 4761 { 4762 numUserAttrs = (numUserAttrs << 8) | 4763 (entryBytes[pos] & 0xFF); 4764 } 4765 } 4766 4767 4768 // Now, we should iterate through the user attributes and decode 4769 // each one. 4770 LinkedHashMap<AttributeType,List<Attribute>> userAttributes = 4771 new LinkedHashMap<AttributeType,List<Attribute>>(); 4772 for (int i=0; i < numUserAttrs; i++) 4773 { 4774 // First, we have the zero-terminated attribute name. 4775 startPos = pos; 4776 while (entryBytes[pos] != 0x00) 4777 { 4778 pos++; 4779 } 4780 name = new String(entryBytes, startPos, pos-startPos, 4781 "UTF-8"); 4782 LinkedHashSet<String> options; 4783 int semicolonPos = name.indexOf(';'); 4784 if (semicolonPos > 0) 4785 { 4786 String baseName = name.substring(0, semicolonPos); 4787 lowerName = toLowerCase(baseName); 4788 options = new LinkedHashSet<String>(); 4789 4790 int nextPos = name.indexOf(';', semicolonPos+1); 4791 while (nextPos > 0) 4792 { 4793 String option = name.substring(semicolonPos+1, nextPos); 4794 if (option.length() > 0) 4795 { 4796 options.add(option); 4797 } 4798 4799 semicolonPos = nextPos; 4800 nextPos = name.indexOf(';', semicolonPos+1); 4801 } 4802 4803 String option = name.substring(semicolonPos+1); 4804 if (option.length() > 0) 4805 { 4806 options.add(option); 4807 } 4808 4809 name = baseName; 4810 } 4811 else 4812 { 4813 lowerName = toLowerCase(name); 4814 options = new LinkedHashSet<String>(0); 4815 } 4816 AttributeType attributeType = 4817 DirectoryServer.getAttributeType(lowerName, true); 4818 4819 4820 4821 // Next, we have the number of values. 4822 int numValues = entryBytes[++pos] & 0x7F; 4823 if (entryBytes[pos++] != numValues) 4824 { 4825 int numLengthBytes = numValues; 4826 numValues = 0; 4827 for (int j=0; j < numLengthBytes; j++, pos++) 4828 { 4829 numValues = (numValues << 8) | (entryBytes[pos] & 0xFF); 4830 } 4831 } 4832 4833 // Next, we have the sequence of length-value pairs. 4834 LinkedHashSet<AttributeValue> values = 4835 new LinkedHashSet<AttributeValue>(numValues); 4836 for (int j=0; j < numValues; j++) 4837 { 4838 int valueLength = entryBytes[pos] & 0x7F; 4839 if (entryBytes[pos++] != valueLength) 4840 { 4841 int numLengthBytes = valueLength; 4842 valueLength = 0; 4843 for (int k=0; k < numLengthBytes; k++, pos++) 4844 { 4845 valueLength = (valueLength << 8) | 4846 (entryBytes[pos] & 0xFF); 4847 } 4848 } 4849 4850 byte[] valueBytes = new byte[valueLength]; 4851 System.arraycopy(entryBytes, pos, valueBytes, 0, 4852 valueLength); 4853 values.add(new AttributeValue(attributeType, 4854 new ASN1OctetString(valueBytes))); 4855 pos += valueLength; 4856 } 4857 4858 4859 // Create the attribute and add it to the set of user 4860 // attributes. 4861 Attribute a = new Attribute(attributeType, name, options, 4862 values); 4863 List<Attribute> attrList = userAttributes.get(attributeType); 4864 if (attrList == null) 4865 { 4866 attrList = new ArrayList<Attribute>(1); 4867 attrList.add(a); 4868 userAttributes.put(attributeType, attrList); 4869 } 4870 else 4871 { 4872 attrList.add(a); 4873 } 4874 } 4875 4876 4877 // Next is the total number of operational attributes. It may 4878 // be a single byte or multiple bytes. 4879 int numOperationalAttrs = entryBytes[pos] & 0x7F; 4880 if (entryBytes[pos++] != numOperationalAttrs) 4881 { 4882 int numLengthBytes = numOperationalAttrs; 4883 numOperationalAttrs = 0; 4884 for (int i=0; i < numLengthBytes; i++, pos++) 4885 { 4886 numOperationalAttrs = 4887 (numOperationalAttrs << 8) | (entryBytes[pos] & 0xFF); 4888 } 4889 } 4890 4891 4892 // Now, we should iterate through the operational attributes and 4893 // decode each one. 4894 LinkedHashMap<AttributeType,List<Attribute>> 4895 operationalAttributes = 4896 new LinkedHashMap<AttributeType,List<Attribute>>(); 4897 for (int i=0; i < numOperationalAttrs; i++) 4898 { 4899 // First, we have the zero-terminated attribute name. 4900 startPos = pos; 4901 while (entryBytes[pos] != 0x00) 4902 { 4903 pos++; 4904 } 4905 name = new String(entryBytes, startPos, pos-startPos, 4906 "UTF-8"); 4907 LinkedHashSet<String> options; 4908 int semicolonPos = name.indexOf(';'); 4909 if (semicolonPos > 0) 4910 { 4911 String baseName = name.substring(0, semicolonPos); 4912 lowerName = toLowerCase(baseName); 4913 options = new LinkedHashSet<String>(); 4914 4915 int nextPos = name.indexOf(';', semicolonPos+1); 4916 while (nextPos > 0) 4917 { 4918 String option = name.substring(semicolonPos+1, nextPos); 4919 if (option.length() > 0) 4920 { 4921 options.add(option); 4922 } 4923 4924 semicolonPos = nextPos; 4925 nextPos = name.indexOf(';', semicolonPos+1); 4926 } 4927 4928 String option = name.substring(semicolonPos+1); 4929 if (option.length() > 0) 4930 { 4931 options.add(option); 4932 } 4933 4934 name = baseName; 4935 } 4936 else 4937 { 4938 lowerName = toLowerCase(name); 4939 options = new LinkedHashSet<String>(0); 4940 } 4941 AttributeType attributeType = 4942 DirectoryServer.getAttributeType(lowerName, true); 4943 4944 4945 // Next, we have the number of values. 4946 int numValues = entryBytes[++pos] & 0x7F; 4947 if (entryBytes[pos++] != numValues) 4948 { 4949 int numLengthBytes = numValues; 4950 numValues = 0; 4951 for (int j=0; j < numLengthBytes; j++, pos++) 4952 { 4953 numValues = (numValues << 8) | (entryBytes[pos] & 0xFF); 4954 } 4955 } 4956 4957 // Next, we have the sequence of length-value pairs. 4958 LinkedHashSet<AttributeValue> values = 4959 new LinkedHashSet<AttributeValue>(numValues); 4960 for (int j=0; j < numValues; j++) 4961 { 4962 int valueLength = entryBytes[pos] & 0x7F; 4963 if (entryBytes[pos++] != valueLength) 4964 { 4965 int numLengthBytes = valueLength; 4966 valueLength = 0; 4967 for (int k=0; k < numLengthBytes; k++, pos++) 4968 { 4969 valueLength = (valueLength << 8) | 4970 (entryBytes[pos] & 0xFF); 4971 } 4972 } 4973 4974 byte[] valueBytes = new byte[valueLength]; 4975 System.arraycopy(entryBytes, pos, valueBytes, 0, 4976 valueLength); 4977 values.add(new AttributeValue(attributeType, 4978 new ASN1OctetString(valueBytes))); 4979 pos += valueLength; 4980 } 4981 4982 4983 // Create the attribute and add it to the set of operational 4984 // attributes. 4985 Attribute a = new Attribute(attributeType, name, options, 4986 values); 4987 List<Attribute> attrList = 4988 operationalAttributes.get(attributeType); 4989 if (attrList == null) 4990 { 4991 attrList = new ArrayList<Attribute>(1); 4992 attrList.add(a); 4993 operationalAttributes.put(attributeType, attrList); 4994 } 4995 else 4996 { 4997 attrList.add(a); 4998 } 4999 } 5000 5001 5002 // We've got everything that we need, so create and return the 5003 // entry. 5004 return new Entry(dn, objectClasses, userAttributes, 5005 operationalAttributes); 5006 } 5007 catch (DirectoryException de) 5008 { 5009 throw de; 5010 } 5011 catch (Exception e) 5012 { 5013 if (debugEnabled()) 5014 { 5015 TRACER.debugCaught(DebugLogLevel.ERROR, e); 5016 } 5017 5018 Message message = 5019 ERR_ENTRY_DECODE_EXCEPTION.get(getExceptionMessage(e)); 5020 throw new DirectoryException( 5021 DirectoryServer.getServerErrorResultCode(), 5022 message, e); 5023 } 5024 } 5025 5026 5027 5028 /** 5029 * Decodes the provided byte array as an entry using the V2 5030 * encoding. 5031 * 5032 * @param entryBytes The byte array containing the data to 5033 * be decoded. 5034 * @param compressedSchema The compressed schema manager to use 5035 * when decoding tokenized schema 5036 * elements. 5037 * 5038 * @return The decoded entry. 5039 * 5040 * @throws DirectoryException If the provided byte array cannot be 5041 * decoded as an entry. 5042 */ 5043 public static Entry decodeV2(byte[] entryBytes, 5044 CompressedSchema compressedSchema) 5045 throws DirectoryException 5046 { 5047 try 5048 { 5049 // The first byte must be the entry version. If it's not one 5050 // we recognize, then that's an error. 5051 if (entryBytes[0] != 0x02) 5052 { 5053 Message message = ERR_ENTRY_DECODE_UNRECOGNIZED_VERSION.get( 5054 byteToHex(entryBytes[0])); 5055 throw new DirectoryException( 5056 DirectoryServer.getServerErrorResultCode(), 5057 message); 5058 } 5059 5060 5061 // Next is the length of the encoded configuration. It may be a 5062 // single byte or multiple bytes. 5063 int pos = 1; 5064 int configLength = entryBytes[pos] & 0x7F; 5065 if (entryBytes[pos++] != configLength) 5066 { 5067 int numLengthBytes = configLength; 5068 configLength = 0; 5069 for (int i=0; i < numLengthBytes; i++, pos++) 5070 { 5071 configLength = 5072 (configLength << 8) | (entryBytes[pos] & 0xFF); 5073 } 5074 } 5075 5076 5077 // Next is the encoded configuration itself. 5078 EntryEncodeConfig config = 5079 EntryEncodeConfig.decode(entryBytes, pos, configLength, 5080 compressedSchema); 5081 pos += configLength; 5082 5083 5084 // If we should have included the DN in the entry, then it's 5085 // next. 5086 DN dn; 5087 if (config.excludeDN()) 5088 { 5089 dn = DN.NULL_DN; 5090 } 5091 else 5092 { 5093 // Next is the length of the DN. It may be a single byte or 5094 // multiple bytes. 5095 int dnLength = entryBytes[pos] & 0x7F; 5096 if (entryBytes[pos++] != dnLength) 5097 { 5098 int numLengthBytes = dnLength; 5099 dnLength = 0; 5100 for (int i=0; i < numLengthBytes; i++, pos++) 5101 { 5102 dnLength = (dnLength << 8) | (entryBytes[pos] & 0xFF); 5103 } 5104 } 5105 5106 5107 // Next is the DN itself. 5108 byte[] dnBytes = new byte[dnLength]; 5109 System.arraycopy(entryBytes, pos, dnBytes, 0, dnLength); 5110 pos += dnLength; 5111 dn = DN.decode(new ASN1OctetString(dnBytes)); 5112 } 5113 5114 5115 // Next is the length of the object classes. It may be a single 5116 // byte or multiple bytes. 5117 int ocLength = entryBytes[pos] & 0x7F; 5118 if (entryBytes[pos++] != ocLength) 5119 { 5120 int numLengthBytes = ocLength; 5121 ocLength = 0; 5122 for (int i=0; i < numLengthBytes; i++, pos++) 5123 { 5124 ocLength = (ocLength << 8) | (entryBytes[pos] & 0xFF); 5125 } 5126 } 5127 5128 5129 // Next is the set of encoded object classes. The encoding will 5130 // depend on the configuration. 5131 Map<ObjectClass,String> objectClasses; 5132 if (config.compressObjectClassSets()) 5133 { 5134 byte[] ocBytes = new byte[ocLength]; 5135 System.arraycopy(entryBytes, pos, ocBytes, 0, ocLength); 5136 objectClasses = config.getCompressedSchema(). 5137 decodeObjectClasses(ocBytes); 5138 pos += ocLength; 5139 } 5140 else 5141 { 5142 // The set of object classes will be encoded as a single 5143 // string with the oibject class names separated by zeros. 5144 objectClasses = new LinkedHashMap<ObjectClass,String>(); 5145 int startPos = pos; 5146 for (int i=0; i < ocLength; i++,pos++) 5147 { 5148 if (entryBytes[pos] == 0x00) 5149 { 5150 String name = new String(entryBytes, startPos, 5151 pos-startPos, "UTF-8"); 5152 String lowerName = toLowerCase(name); 5153 ObjectClass oc = 5154 DirectoryServer.getObjectClass(lowerName, true); 5155 objectClasses.put(oc, name); 5156 startPos = pos+1; 5157 } 5158 } 5159 String name = new String(entryBytes, startPos, pos-startPos, 5160 "UTF-8"); 5161 String lowerName = toLowerCase(name); 5162 ObjectClass oc = 5163 DirectoryServer.getObjectClass(lowerName, true); 5164 objectClasses.put(oc, name); 5165 } 5166 5167 5168 // Next is the total number of user attributes. It may be a 5169 // single byte or multiple bytes. 5170 int numUserAttrs = entryBytes[pos] & 0x7F; 5171 if (entryBytes[pos++] != numUserAttrs) 5172 { 5173 int numLengthBytes = numUserAttrs; 5174 numUserAttrs = 0; 5175 for (int i=0; i < numLengthBytes; i++, pos++) 5176 { 5177 numUserAttrs = (numUserAttrs << 8) | 5178 (entryBytes[pos] & 0xFF); 5179 } 5180 } 5181 5182 5183 // Now, we should iterate through the user attributes and decode 5184 // each one. 5185 LinkedHashMap<AttributeType,List<Attribute>> userAttributes = 5186 new LinkedHashMap<AttributeType,List<Attribute>>(); 5187 if (config.compressAttributeDescriptions()) 5188 { 5189 for (int i=0; i < numUserAttrs; i++) 5190 { 5191 // Get the length of the attribute. 5192 int attrLength = entryBytes[pos] & 0x7F; 5193 if (entryBytes[pos++] != attrLength) 5194 { 5195 int attrLengthBytes = attrLength; 5196 attrLength = 0; 5197 for (int j=0; j < attrLengthBytes; j++, pos++) 5198 { 5199 attrLength = (attrLength << 8) | 5200 (entryBytes[pos] & 0xFF); 5201 } 5202 } 5203 5204 // Decode the attribute. 5205 Attribute a = config.getCompressedSchema().decodeAttribute( 5206 entryBytes, pos, attrLength); 5207 List<Attribute> attrList = 5208 userAttributes.get(a.getAttributeType()); 5209 if (attrList == null) 5210 { 5211 attrList = new ArrayList<Attribute>(1); 5212 userAttributes.put(a.getAttributeType(), attrList); 5213 } 5214 5215 attrList.add(a); 5216 pos += attrLength; 5217 } 5218 } 5219 else 5220 { 5221 for (int i=0; i < numUserAttrs; i++) 5222 { 5223 // First, we have the zero-terminated attribute name. 5224 int startPos = pos; 5225 while (entryBytes[pos] != 0x00) 5226 { 5227 pos++; 5228 } 5229 5230 String lowerName; 5231 String name = new String(entryBytes, startPos, pos-startPos, 5232 "UTF-8"); 5233 LinkedHashSet<String> options; 5234 int semicolonPos = name.indexOf(';'); 5235 if (semicolonPos > 0) 5236 { 5237 String baseName = name.substring(0, semicolonPos); 5238 lowerName = toLowerCase(baseName); 5239 options = new LinkedHashSet<String>(); 5240 5241 int nextPos = name.indexOf(';', semicolonPos+1); 5242 while (nextPos > 0) 5243 { 5244 String option = name.substring(semicolonPos+1, nextPos); 5245 if (option.length() > 0) 5246 { 5247 options.add(option); 5248 } 5249 5250 semicolonPos = nextPos; 5251 nextPos = name.indexOf(';', semicolonPos+1); 5252 } 5253 5254 String option = name.substring(semicolonPos+1); 5255 if (option.length() > 0) 5256 { 5257 options.add(option); 5258 } 5259 5260 name = baseName; 5261 } 5262 else 5263 { 5264 lowerName = toLowerCase(name); 5265 options = new LinkedHashSet<String>(0); 5266 } 5267 AttributeType attributeType = 5268 DirectoryServer.getAttributeType(lowerName, true); 5269 5270 5271 5272 // Next, we have the number of values. 5273 int numValues = entryBytes[++pos] & 0x7F; 5274 if (entryBytes[pos++] != numValues) 5275 { 5276 int numLengthBytes = numValues; 5277 numValues = 0; 5278 for (int j=0; j < numLengthBytes; j++, pos++) 5279 { 5280 numValues = (numValues << 8) | (entryBytes[pos] & 0xFF); 5281 } 5282 } 5283 5284 // Next, we have the sequence of length-value pairs. 5285 LinkedHashSet<AttributeValue> values = 5286 new LinkedHashSet<AttributeValue>(numValues); 5287 for (int j=0; j < numValues; j++) 5288 { 5289 int valueLength = entryBytes[pos] & 0x7F; 5290 if (entryBytes[pos++] != valueLength) 5291 { 5292 int numLengthBytes = valueLength; 5293 valueLength = 0; 5294 for (int k=0; k < numLengthBytes; k++, pos++) 5295 { 5296 valueLength = (valueLength << 8) | 5297 (entryBytes[pos] & 0xFF); 5298 } 5299 } 5300 5301 byte[] valueBytes = new byte[valueLength]; 5302 System.arraycopy(entryBytes, pos, valueBytes, 0, 5303 valueLength); 5304 values.add(new AttributeValue(attributeType, 5305 new ASN1OctetString(valueBytes))); 5306 pos += valueLength; 5307 } 5308 5309 5310 // Create the attribute and add it to the set of user 5311 // attributes. 5312 Attribute a = new Attribute(attributeType, name, options, 5313 values); 5314 List<Attribute> attrList = 5315 userAttributes.get(attributeType); 5316 if (attrList == null) 5317 { 5318 attrList = new ArrayList<Attribute>(1); 5319 attrList.add(a); 5320 userAttributes.put(attributeType, attrList); 5321 } 5322 else 5323 { 5324 attrList.add(a); 5325 } 5326 } 5327 } 5328 5329 5330 // Next is the total number of operational attributes. It may 5331 // be a single byte or multiple bytes. 5332 int numOperationalAttrs = entryBytes[pos] & 0x7F; 5333 if (entryBytes[pos++] != numOperationalAttrs) 5334 { 5335 int numLengthBytes = numOperationalAttrs; 5336 numOperationalAttrs = 0; 5337 for (int i=0; i < numLengthBytes; i++, pos++) 5338 { 5339 numOperationalAttrs = 5340 (numOperationalAttrs << 8) | (entryBytes[pos] & 0xFF); 5341 } 5342 } 5343 5344 5345 // Now, we should iterate through the operational attributes and 5346 // decode each one. 5347 LinkedHashMap<AttributeType,List<Attribute>> 5348 operationalAttributes = 5349 new LinkedHashMap<AttributeType,List<Attribute>>(); 5350 if (config.compressAttributeDescriptions()) 5351 { 5352 for (int i=0; i < numOperationalAttrs; i++) 5353 { 5354 // Get the length of the attribute. 5355 int attrLength = entryBytes[pos] & 0x7F; 5356 if (entryBytes[pos++] != attrLength) 5357 { 5358 int attrLengthBytes = attrLength; 5359 attrLength = 0; 5360 for (int j=0; j < attrLengthBytes; j++, pos++) 5361 { 5362 attrLength = (attrLength << 8) | 5363 (entryBytes[pos] & 0xFF); 5364 } 5365 } 5366 5367 // Decode the attribute. 5368 Attribute a = config.getCompressedSchema().decodeAttribute( 5369 entryBytes, pos, attrLength); 5370 List<Attribute> attrList = 5371 operationalAttributes.get(a.getAttributeType()); 5372 if (attrList == null) 5373 { 5374 attrList = new ArrayList<Attribute>(1); 5375 operationalAttributes.put(a.getAttributeType(), attrList); 5376 } 5377 5378 attrList.add(a); 5379 pos += attrLength; 5380 } 5381 } 5382 else 5383 { 5384 for (int i=0; i < numOperationalAttrs; i++) 5385 { 5386 // First, we have the zero-terminated attribute name. 5387 int startPos = pos; 5388 while (entryBytes[pos] != 0x00) 5389 { 5390 pos++; 5391 } 5392 String lowerName; 5393 String name = new String(entryBytes, startPos, pos-startPos, 5394 "UTF-8"); 5395 LinkedHashSet<String> options; 5396 int semicolonPos = name.indexOf(';'); 5397 if (semicolonPos > 0) 5398 { 5399 String baseName = name.substring(0, semicolonPos); 5400 lowerName = toLowerCase(baseName); 5401 options = new LinkedHashSet<String>(); 5402 5403 int nextPos = name.indexOf(';', semicolonPos+1); 5404 while (nextPos > 0) 5405 { 5406 String option = name.substring(semicolonPos+1, nextPos); 5407 if (option.length() > 0) 5408 { 5409 options.add(option); 5410 } 5411 5412 semicolonPos = nextPos; 5413 nextPos = name.indexOf(';', semicolonPos+1); 5414 } 5415 5416 String option = name.substring(semicolonPos+1); 5417 if (option.length() > 0) 5418 { 5419 options.add(option); 5420 } 5421 5422 name = baseName; 5423 } 5424 else 5425 { 5426 lowerName = toLowerCase(name); 5427 options = new LinkedHashSet<String>(0); 5428 } 5429 AttributeType attributeType = 5430 DirectoryServer.getAttributeType(lowerName, true); 5431 5432 5433 // Next, we have the number of values. 5434 int numValues = entryBytes[++pos] & 0x7F; 5435 if (entryBytes[pos++] != numValues) 5436 { 5437 int numLengthBytes = numValues; 5438 numValues = 0; 5439 for (int j=0; j < numLengthBytes; j++, pos++) 5440 { 5441 numValues = (numValues << 8) | (entryBytes[pos] & 0xFF); 5442 } 5443 } 5444 5445 // Next, we have the sequence of length-value pairs. 5446 LinkedHashSet<AttributeValue> values = 5447 new LinkedHashSet<AttributeValue>(numValues); 5448 for (int j=0; j < numValues; j++) 5449 { 5450 int valueLength = entryBytes[pos] & 0x7F; 5451 if (entryBytes[pos++] != valueLength) 5452 { 5453 int numLengthBytes = valueLength; 5454 valueLength = 0; 5455 for (int k=0; k < numLengthBytes; k++, pos++) 5456 { 5457 valueLength = (valueLength << 8) | 5458 (entryBytes[pos] & 0xFF); 5459 } 5460 } 5461 5462 byte[] valueBytes = new byte[valueLength]; 5463 System.arraycopy(entryBytes, pos, valueBytes, 0, 5464 valueLength); 5465 values.add(new AttributeValue(attributeType, 5466 new ASN1OctetString(valueBytes))); 5467 pos += valueLength; 5468 } 5469 5470 5471 // Create the attribute and add it to the set of operational 5472 // attributes. 5473 Attribute a = new Attribute(attributeType, name, options, 5474 values); 5475 List<Attribute> attrList = 5476 operationalAttributes.get(attributeType); 5477 if (attrList == null) 5478 { 5479 attrList = new ArrayList<Attribute>(1); 5480 attrList.add(a); 5481 operationalAttributes.put(attributeType, attrList); 5482 } 5483 else 5484 { 5485 attrList.add(a); 5486 } 5487 } 5488 } 5489 5490 5491 // We've got everything that we need, so create and return the 5492 // entry. 5493 return new Entry(dn, objectClasses, userAttributes, 5494 operationalAttributes); 5495 } 5496 catch (DirectoryException de) 5497 { 5498 throw de; 5499 } 5500 catch (Exception e) 5501 { 5502 if (debugEnabled()) 5503 { 5504 TRACER.debugCaught(DebugLogLevel.ERROR, e); 5505 } 5506 5507 Message message = 5508 ERR_ENTRY_DECODE_EXCEPTION.get(getExceptionMessage(e)); 5509 throw new DirectoryException( 5510 DirectoryServer.getServerErrorResultCode(), 5511 message, e); 5512 } 5513 } 5514 5515 5516 5517 /** 5518 * Retrieves a list of the lines for this entry in LDIF form. Long 5519 * lines will not be wrapped automatically. 5520 * 5521 * @return A list of the lines for this entry in LDIF form. 5522 */ 5523 public List<StringBuilder> toLDIF() 5524 { 5525 LinkedList<StringBuilder> ldifLines = 5526 new LinkedList<StringBuilder>(); 5527 5528 5529 // First, append the DN. 5530 StringBuilder dnLine = new StringBuilder(); 5531 dnLine.append("dn"); 5532 appendLDIFSeparatorAndValue(dnLine, getBytes(dn.toString())); 5533 ldifLines.add(dnLine); 5534 5535 5536 // Next, add the set of objectclasses. 5537 for (String s : objectClasses.values()) 5538 { 5539 StringBuilder ocLine = new StringBuilder(); 5540 ocLine.append("objectClass: "); 5541 ocLine.append(s); 5542 ldifLines.add(ocLine); 5543 } 5544 5545 5546 // Next, add the set of user attributes. 5547 for (List<Attribute> attrList : userAttributes.values()) 5548 { 5549 for (Attribute a : attrList) 5550 { 5551 StringBuilder attrName = new StringBuilder(a.getName()); 5552 for (String o : a.getOptions()) 5553 { 5554 attrName.append(";"); 5555 attrName.append(o); 5556 } 5557 5558 for (AttributeValue v : a.getValues()) 5559 { 5560 StringBuilder attrLine = new StringBuilder(); 5561 attrLine.append(attrName); 5562 appendLDIFSeparatorAndValue(attrLine, v.getValueBytes()); 5563 ldifLines.add(attrLine); 5564 } 5565 } 5566 } 5567 5568 5569 // Finally, add the set of operational attributes. 5570 for (List<Attribute> attrList : operationalAttributes.values()) 5571 { 5572 for (Attribute a : attrList) 5573 { 5574 StringBuilder attrName = new StringBuilder(a.getName()); 5575 for (String o : a.getOptions()) 5576 { 5577 attrName.append(";"); 5578 attrName.append(o); 5579 } 5580 5581 for (AttributeValue v : a.getValues()) 5582 { 5583 StringBuilder attrLine = new StringBuilder(); 5584 attrLine.append(attrName); 5585 appendLDIFSeparatorAndValue(attrLine, v.getValueBytes()); 5586 ldifLines.add(attrLine); 5587 } 5588 } 5589 } 5590 5591 5592 return ldifLines; 5593 } 5594 5595 5596 5597 /** 5598 * Writes this entry in LDIF form according to the provided 5599 * configuration. 5600 * 5601 * @param exportConfig The configuration that specifies how the 5602 * entry should be written. 5603 * 5604 * @return <CODE>true</CODE> if the entry is actually written, or 5605 * <CODE>false</CODE> if it is not for some reason. 5606 * 5607 * @throws IOException If a problem occurs while writing the 5608 * information. 5609 * 5610 * @throws LDIFException If a problem occurs while trying to 5611 * determine whether to write the entry. 5612 */ 5613 public boolean toLDIF(LDIFExportConfig exportConfig) 5614 throws IOException, LDIFException 5615 { 5616 // See if this entry should be included in the export at all. 5617 try 5618 { 5619 if (! exportConfig.includeEntry(this)) 5620 { 5621 if (debugEnabled()) 5622 { 5623 TRACER.debugInfo( 5624 "Skipping entry %s because of the export " + 5625 "configuration.", String.valueOf(dn)); 5626 } 5627 return false; 5628 } 5629 } 5630 catch (Exception e) 5631 { 5632 if (debugEnabled()) 5633 { 5634 TRACER.debugCaught(DebugLogLevel.ERROR, e); 5635 } 5636 5637 Message message = 5638 ERR_LDIF_COULD_NOT_EVALUATE_FILTERS_FOR_EXPORT. 5639 get(String.valueOf(dn), String.valueOf(e)); 5640 throw new LDIFException(message, e); 5641 } 5642 5643 5644 // Invoke LDIF export plugins on the entry if appropriate. 5645 if (exportConfig.invokeExportPlugins()) 5646 { 5647 PluginConfigManager pluginConfigManager = 5648 DirectoryServer.getPluginConfigManager(); 5649 PluginResult.ImportLDIF pluginResult = 5650 pluginConfigManager.invokeLDIFExportPlugins(exportConfig, 5651 this); 5652 if (! pluginResult.continueProcessing()) 5653 { 5654 return false; 5655 } 5656 } 5657 5658 5659 // Get the information necessary to write the LDIF. 5660 BufferedWriter writer = exportConfig.getWriter(); 5661 int wrapColumn = exportConfig.getWrapColumn(); 5662 boolean wrapLines = (wrapColumn > 1); 5663 5664 5665 // First, write the DN. It will always be included. 5666 StringBuilder dnLine = new StringBuilder(); 5667 dnLine.append("dn"); 5668 appendLDIFSeparatorAndValue(dnLine, getBytes(dn.toString())); 5669 writeLDIFLine(dnLine, writer, wrapLines, wrapColumn); 5670 5671 5672 // Next, the set of objectclasses. 5673 final boolean typesOnly = exportConfig.typesOnly(); 5674 if (exportConfig.includeObjectClasses()) 5675 { 5676 if (typesOnly) 5677 { 5678 StringBuilder ocLine = new StringBuilder("objectClass:"); 5679 writeLDIFLine(ocLine, writer, wrapLines, wrapColumn); 5680 } 5681 else 5682 { 5683 for (String s : objectClasses.values()) 5684 { 5685 StringBuilder ocLine = new StringBuilder(); 5686 ocLine.append("objectClass: "); 5687 ocLine.append(s); 5688 writeLDIFLine(ocLine, writer, wrapLines, wrapColumn); 5689 } 5690 } 5691 } 5692 else 5693 { 5694 if (debugEnabled()) 5695 { 5696 TRACER.debugVerbose( 5697 "Skipping objectclasses for entry %s because of " + 5698 "the export configuration.", String.valueOf(dn)); 5699 } 5700 } 5701 5702 5703 // Now the set of user attributes. 5704 for (AttributeType attrType : userAttributes.keySet()) 5705 { 5706 if (exportConfig.includeAttribute(attrType)) 5707 { 5708 List<Attribute> attrList = userAttributes.get(attrType); 5709 for (Attribute a : attrList) 5710 { 5711 if (a.isVirtual() && 5712 (! exportConfig.includeVirtualAttributes())) 5713 { 5714 continue; 5715 } 5716 5717 if (typesOnly) 5718 { 5719 StringBuilder attrName = new StringBuilder(a.getName()); 5720 for (String o : a.getOptions()) 5721 { 5722 attrName.append(";"); 5723 attrName.append(o); 5724 } 5725 attrName.append(":"); 5726 5727 writeLDIFLine(attrName, writer, wrapLines, wrapColumn); 5728 } 5729 else 5730 { 5731 StringBuilder attrName = new StringBuilder(a.getName()); 5732 for (String o : a.getOptions()) 5733 { 5734 attrName.append(";"); 5735 attrName.append(o); 5736 } 5737 5738 for (AttributeValue v : a.getValues()) 5739 { 5740 StringBuilder attrLine = new StringBuilder(); 5741 attrLine.append(attrName); 5742 appendLDIFSeparatorAndValue(attrLine, 5743 v.getValueBytes()); 5744 writeLDIFLine(attrLine, writer, wrapLines, wrapColumn); 5745 } 5746 } 5747 } 5748 } 5749 else 5750 { 5751 if (debugEnabled()) 5752 { 5753 TRACER.debugVerbose( 5754 "Skipping user attribute %s for entry %s because of " + 5755 "the export configuration.", 5756 attrType.getNameOrOID(), String.valueOf(dn)); 5757 } 5758 } 5759 } 5760 5761 5762 // Next, the set of operational attributes. 5763 if (exportConfig.includeOperationalAttributes()) 5764 { 5765 for (AttributeType attrType : operationalAttributes.keySet()) 5766 { 5767 if (exportConfig.includeAttribute(attrType)) 5768 { 5769 List<Attribute> attrList = 5770 operationalAttributes.get(attrType); 5771 for (Attribute a : attrList) 5772 { 5773 if (a.isVirtual() && 5774 (! exportConfig.includeVirtualAttributes())) 5775 { 5776 continue; 5777 } 5778 5779 if (typesOnly) 5780 { 5781 StringBuilder attrName = new StringBuilder(a.getName()); 5782 for (String o : a.getOptions()) 5783 { 5784 attrName.append(";"); 5785 attrName.append(o); 5786 } 5787 attrName.append(":"); 5788 5789 writeLDIFLine(attrName, writer, wrapLines, wrapColumn); 5790 } 5791 else 5792 { 5793 StringBuilder attrName = new StringBuilder(a.getName()); 5794 for (String o : a.getOptions()) 5795 { 5796 attrName.append(";"); 5797 attrName.append(o); 5798 } 5799 5800 for (AttributeValue v : a.getValues()) 5801 { 5802 StringBuilder attrLine = new StringBuilder(); 5803 attrLine.append(attrName); 5804 appendLDIFSeparatorAndValue(attrLine, 5805 v.getValueBytes()); 5806 writeLDIFLine(attrLine, writer, wrapLines, 5807 wrapColumn); 5808 } 5809 } 5810 } 5811 } 5812 else 5813 { 5814 if (debugEnabled()) 5815 { 5816 TRACER.debugVerbose( 5817 "Skipping operational attribute %s for entry %s " + 5818 "because of the export configuration.", 5819 attrType.getNameOrOID(), String.valueOf(dn)); 5820 } 5821 } 5822 } 5823 } 5824 else 5825 { 5826 if (debugEnabled()) 5827 { 5828 TRACER.debugVerbose( 5829 "Skipping all operational attributes for entry %s " + 5830 "because of the export configuration.", 5831 String.valueOf(dn)); 5832 } 5833 } 5834 5835 5836 // If we are not supposed to include virtual attributes, then 5837 // write any attributes that may normally be suppressed by a 5838 // virtual attribute. 5839 if (! exportConfig.includeVirtualAttributes()) 5840 { 5841 for (AttributeType t : suppressedAttributes.keySet()) 5842 { 5843 if (exportConfig.includeAttribute(t)) 5844 { 5845 for (Attribute a : suppressedAttributes.get(t)) 5846 { 5847 if (typesOnly) 5848 { 5849 StringBuilder attrName = new StringBuilder(a.getName()); 5850 for (String o : a.getOptions()) 5851 { 5852 attrName.append(";"); 5853 attrName.append(o); 5854 } 5855 attrName.append(":"); 5856 5857 writeLDIFLine(attrName, writer, wrapLines, wrapColumn); 5858 } 5859 else 5860 { 5861 StringBuilder attrName = new StringBuilder(a.getName()); 5862 for (String o : a.getOptions()) 5863 { 5864 attrName.append(";"); 5865 attrName.append(o); 5866 } 5867 5868 for (AttributeValue v : a.getValues()) 5869 { 5870 StringBuilder attrLine = new StringBuilder(); 5871 attrLine.append(attrName); 5872 appendLDIFSeparatorAndValue(attrLine, 5873 v.getValueBytes()); 5874 writeLDIFLine(attrLine, writer, wrapLines, 5875 wrapColumn); 5876 } 5877 } 5878 } 5879 } 5880 } 5881 } 5882 5883 5884 // Make sure there is a blank line after the entry. 5885 writer.newLine(); 5886 5887 5888 return true; 5889 } 5890 5891 5892 5893 /** 5894 * Retrieves the name of the protocol associated with this protocol 5895 * element. 5896 * 5897 * @return The name of the protocol associated with this protocol 5898 * element. 5899 */ 5900 public String getProtocolElementName() 5901 { 5902 return "Entry"; 5903 } 5904 5905 5906 5907 /** 5908 * Retrieves a hash code for this entry. 5909 * 5910 * @return The hash code for this entry. 5911 */ 5912 @Override() 5913 public int hashCode() 5914 { 5915 int hashCode = dn.hashCode(); 5916 5917 for (ObjectClass oc : objectClasses.keySet()) 5918 { 5919 hashCode += oc.hashCode(); 5920 } 5921 5922 for (List<Attribute> attrList : userAttributes.values()) 5923 { 5924 for (Attribute a : attrList) 5925 { 5926 hashCode += a.hashCode(); 5927 } 5928 } 5929 5930 for (List<Attribute> attrList : operationalAttributes.values()) 5931 { 5932 for (Attribute a : attrList) 5933 { 5934 hashCode += a.hashCode(); 5935 } 5936 } 5937 5938 return hashCode; 5939 } 5940 5941 5942 5943 /** 5944 * Indicates whether the provided object is equal to this entry. In 5945 * order for the object to be considered equal, it must be an entry 5946 * with the same DN, set of object classes, and set of user and 5947 * operational attributes. 5948 * 5949 * @param o The object for which to make the determination. 5950 * 5951 * @return {@code true} if the provided object may be considered 5952 * equal to this entry, or {@code false} if not. 5953 */ 5954 @Override() 5955 public boolean equals(Object o) 5956 { 5957 if (this == o) 5958 { 5959 return true; 5960 } 5961 5962 if (o == null) 5963 { 5964 return false; 5965 } 5966 5967 if (! (o instanceof Entry)) 5968 { 5969 return false; 5970 } 5971 5972 Entry e = (Entry) o; 5973 if (! dn.equals(e.dn)) 5974 { 5975 return false; 5976 } 5977 5978 if (! objectClasses.keySet().equals(e.objectClasses.keySet())) 5979 { 5980 return false; 5981 } 5982 5983 for (AttributeType at : userAttributes.keySet()) 5984 { 5985 List<Attribute> list1 = userAttributes.get(at); 5986 List<Attribute> list2 = e.userAttributes.get(at); 5987 if ((list2 == null) || (list1.size() != list2.size())) 5988 { 5989 return false; 5990 } 5991 for (Attribute a : list1) 5992 { 5993 if (! list2.contains(a)) 5994 { 5995 return false; 5996 } 5997 } 5998 } 5999 6000 for (AttributeType at : operationalAttributes.keySet()) 6001 { 6002 List<Attribute> list1 = operationalAttributes.get(at); 6003 List<Attribute> list2 = e.operationalAttributes.get(at); 6004 if ((list2 == null) || (list1.size() != list2.size())) 6005 { 6006 return false; 6007 } 6008 for (Attribute a : list1) 6009 { 6010 if (! list2.contains(a)) 6011 { 6012 return false; 6013 } 6014 } 6015 } 6016 6017 return true; 6018 } 6019 6020 6021 6022 /** 6023 * Retrieves a string representation of this protocol element. 6024 * 6025 * @return A string representation of this protocol element. 6026 */ 6027 public String toString() 6028 { 6029 StringBuilder buffer = new StringBuilder(); 6030 toString(buffer); 6031 return buffer.toString(); 6032 } 6033 6034 6035 6036 /** 6037 * Appends a string representation of this protocol element to the 6038 * provided buffer. 6039 * 6040 * @param buffer The buffer into which the string representation 6041 * should be written. 6042 */ 6043 public void toString(StringBuilder buffer) 6044 { 6045 buffer.append("Entry(dn=\""); 6046 dn.toString(buffer); 6047 6048 buffer.append("\", objectClasses={"); 6049 if (! objectClasses.isEmpty()) 6050 { 6051 Iterator<String> ocNames = objectClasses.values().iterator(); 6052 buffer.append(ocNames.next()); 6053 6054 while (ocNames.hasNext()) 6055 { 6056 buffer.append(","); 6057 buffer.append(ocNames.next()); 6058 } 6059 } 6060 6061 buffer.append("}, userAttrs={"); 6062 if (! userAttributes.isEmpty()) 6063 { 6064 Iterator<AttributeType> attrs = 6065 userAttributes.keySet().iterator(); 6066 buffer.append(attrs.next().getNameOrOID()); 6067 6068 while (attrs.hasNext()) 6069 { 6070 buffer.append(","); 6071 buffer.append(attrs.next().getNameOrOID()); 6072 } 6073 } 6074 6075 buffer.append("}, operationalAttrs={"); 6076 if (! operationalAttributes.isEmpty()) 6077 { 6078 Iterator<AttributeType> attrs = 6079 operationalAttributes.keySet().iterator(); 6080 buffer.append(attrs.next().getNameOrOID()); 6081 6082 while (attrs.hasNext()) 6083 { 6084 buffer.append(","); 6085 buffer.append(attrs.next().getNameOrOID()); 6086 } 6087 } 6088 6089 buffer.append("})"); 6090 } 6091 6092 6093 6094 /** 6095 * Appends a string representation of this protocol element to the 6096 * provided buffer. 6097 * 6098 * @param buffer The buffer into which the string representation 6099 * should be written. 6100 * @param indent The number of spaces that should be used to 6101 * indent the resulting string representation. 6102 */ 6103 public void toString(StringBuilder buffer, int indent) 6104 { 6105 StringBuilder indentBuf = new StringBuilder(indent); 6106 for (int i=0 ; i < indent; i++) 6107 { 6108 indentBuf.append(' '); 6109 } 6110 6111 for (StringBuilder b : toLDIF()) 6112 { 6113 buffer.append(indentBuf); 6114 buffer.append(b); 6115 buffer.append(EOL); 6116 } 6117 } 6118 6119 6120 6121 /** 6122 * Retrieves a string representation of this entry in LDIF form. 6123 * 6124 * @return A string representation of this entry in LDIF form. 6125 */ 6126 public String toLDIFString() 6127 { 6128 StringBuilder buffer = new StringBuilder(); 6129 6130 for (StringBuilder ldifLine : toLDIF()) 6131 { 6132 buffer.append(ldifLine); 6133 buffer.append(EOL); 6134 } 6135 6136 return buffer.toString(); 6137 } 6138 6139 6140 6141 /** 6142 * Retrieves a one-line representation of this entry. 6143 * 6144 * @return A one-line representation of this entry. 6145 */ 6146 public String toSingleLineString() 6147 { 6148 StringBuilder buffer = new StringBuilder(); 6149 toSingleLineString(buffer); 6150 return buffer.toString(); 6151 } 6152 6153 6154 6155 /** 6156 * Appends a single-line representation of this entry to the 6157 * provided buffer. 6158 * 6159 * @param buffer The buffer to which the information should be 6160 * written. 6161 */ 6162 public void toSingleLineString(StringBuilder buffer) 6163 { 6164 buffer.append("Entry(dn=\""); 6165 dn.toString(buffer); 6166 buffer.append("\",objectClasses={"); 6167 6168 Iterator<String> iterator = objectClasses.values().iterator(); 6169 if (iterator.hasNext()) 6170 { 6171 buffer.append(iterator.next()); 6172 6173 while (iterator.hasNext()) 6174 { 6175 buffer.append(","); 6176 buffer.append(iterator.next()); 6177 } 6178 } 6179 6180 buffer.append("},userAttrs={"); 6181 6182 boolean firstAttr = true; 6183 for (List<Attribute> attrList : userAttributes.values()) 6184 { 6185 for (Attribute a : attrList) 6186 { 6187 if (firstAttr) 6188 { 6189 firstAttr = false; 6190 } 6191 else 6192 { 6193 buffer.append(","); 6194 } 6195 6196 buffer.append(a.getName()); 6197 6198 if (a.hasOptions()) 6199 { 6200 for (String optionString : a.getOptions()) 6201 { 6202 buffer.append(";"); 6203 buffer.append(optionString); 6204 } 6205 } 6206 6207 buffer.append("={"); 6208 Iterator<AttributeValue> valueIterator = 6209 a.getValues().iterator(); 6210 if (valueIterator.hasNext()) 6211 { 6212 buffer.append(valueIterator.next().getStringValue()); 6213 6214 while (valueIterator.hasNext()) 6215 { 6216 buffer.append(","); 6217 buffer.append(valueIterator.next().getStringValue()); 6218 } 6219 } 6220 6221 buffer.append("}"); 6222 } 6223 } 6224 6225 buffer.append("},operationalAttrs={"); 6226 for (List<Attribute> attrList : operationalAttributes.values()) 6227 { 6228 for (Attribute a : attrList) 6229 { 6230 if (firstAttr) 6231 { 6232 firstAttr = false; 6233 } 6234 else 6235 { 6236 buffer.append(","); 6237 } 6238 6239 buffer.append(a.getName()); 6240 6241 if (a.hasOptions()) 6242 { 6243 for (String optionString : a.getOptions()) 6244 { 6245 buffer.append(";"); 6246 buffer.append(optionString); 6247 } 6248 } 6249 6250 buffer.append("={"); 6251 Iterator<AttributeValue> valueIterator = 6252 a.getValues().iterator(); 6253 if (valueIterator.hasNext()) 6254 { 6255 buffer.append(valueIterator.next().getStringValue()); 6256 6257 while (valueIterator.hasNext()) 6258 { 6259 buffer.append(","); 6260 buffer.append(valueIterator.next().getStringValue()); 6261 } 6262 } 6263 6264 buffer.append("}"); 6265 } 6266 } 6267 6268 buffer.append("})"); 6269 } 6270 } 6271