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.schema; 028 029 030 031 import java.text.SimpleDateFormat; 032 import java.util.Date; 033 import java.util.TimeZone; 034 035 import org.opends.messages.Message; 036 import org.opends.messages.MessageBuilder; 037 import org.opends.server.admin.std.server.AttributeSyntaxCfg; 038 import org.opends.server.api.ApproximateMatchingRule; 039 import org.opends.server.api.AttributeSyntax; 040 import org.opends.server.api.EqualityMatchingRule; 041 import org.opends.server.api.OrderingMatchingRule; 042 import org.opends.server.api.SubstringMatchingRule; 043 import org.opends.server.config.ConfigException; 044 import org.opends.server.core.DirectoryServer; 045 import org.opends.server.loggers.debug.DebugTracer; 046 import org.opends.server.protocols.asn1.ASN1OctetString; 047 import org.opends.server.types.AttributeValue; 048 import org.opends.server.types.ByteString; 049 import org.opends.server.types.DebugLogLevel; 050 import org.opends.server.types.DirectoryException; 051 import org.opends.server.types.ResultCode; 052 053 import static org.opends.messages.SchemaMessages.*; 054 import static org.opends.server.loggers.debug.DebugLogger.*; 055 import static org.opends.server.loggers.ErrorLogger.*; 056 import static org.opends.server.schema.SchemaConstants.*; 057 import static org.opends.server.util.ServerConstants.*; 058 059 060 061 /** 062 * This class implements the UTC time attribute syntax. This is very similar to 063 * the generalized time syntax (and actually has been deprecated in favor of 064 * that), but requires that the minute be provided and does not allow for 065 * sub-second times. All matching will be performed using the generalized time 066 * matching rules, and equality, ordering, and substring matching will be 067 * allowed. 068 */ 069 public class UTCTimeSyntax 070 extends AttributeSyntax<AttributeSyntaxCfg> 071 { 072 /** 073 * The tracer object for the debug logger. 074 */ 075 private static final DebugTracer TRACER = getTracer(); 076 077 078 079 /** 080 * The lock that will be used to provide threadsafe access to the date 081 * formatter. 082 */ 083 private static Object dateFormatLock; 084 085 086 087 /** 088 * The date formatter that will be used to convert dates into UTC time values. 089 * Note that all interaction with it must be synchronized. 090 */ 091 private static SimpleDateFormat dateFormat; 092 093 094 095 // The default equality matching rule for this syntax. 096 private EqualityMatchingRule defaultEqualityMatchingRule; 097 098 // The default ordering matching rule for this syntax. 099 private OrderingMatchingRule defaultOrderingMatchingRule; 100 101 // The default substring matching rule for this syntax. 102 private SubstringMatchingRule defaultSubstringMatchingRule; 103 104 105 106 /* 107 * Create the date formatter that will be used to construct and parse 108 * normalized UTC time values. 109 */ 110 static 111 { 112 dateFormat = new SimpleDateFormat(DATE_FORMAT_UTC_TIME); 113 dateFormat.setLenient(false); 114 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 115 116 dateFormatLock = new Object(); 117 } 118 119 120 121 /** 122 * Creates a new instance of this syntax. Note that the only thing that 123 * should be done here is to invoke the default constructor for the 124 * superclass. All initialization should be performed in the 125 * <CODE>initializeSyntax</CODE> method. 126 */ 127 public UTCTimeSyntax() 128 { 129 super(); 130 } 131 132 133 134 /** 135 * {@inheritDoc} 136 */ 137 public void initializeSyntax(AttributeSyntaxCfg configuration) 138 throws ConfigException 139 { 140 defaultEqualityMatchingRule = 141 DirectoryServer.getEqualityMatchingRule(EMR_GENERALIZED_TIME_OID); 142 if (defaultEqualityMatchingRule == null) 143 { 144 logError(ERR_ATTR_SYNTAX_UNKNOWN_EQUALITY_MATCHING_RULE.get( 145 EMR_GENERALIZED_TIME_OID, SYNTAX_UTC_TIME_NAME)); 146 } 147 148 defaultOrderingMatchingRule = 149 DirectoryServer.getOrderingMatchingRule(OMR_GENERALIZED_TIME_OID); 150 if (defaultOrderingMatchingRule == null) 151 { 152 logError(ERR_ATTR_SYNTAX_UNKNOWN_ORDERING_MATCHING_RULE.get( 153 OMR_GENERALIZED_TIME_OID, SYNTAX_UTC_TIME_NAME)); 154 } 155 156 defaultSubstringMatchingRule = 157 DirectoryServer.getSubstringMatchingRule(SMR_CASE_IGNORE_OID); 158 if (defaultSubstringMatchingRule == null) 159 { 160 logError(ERR_ATTR_SYNTAX_UNKNOWN_SUBSTRING_MATCHING_RULE.get( 161 SMR_CASE_IGNORE_OID, SYNTAX_UTC_TIME_NAME)); 162 } 163 } 164 165 166 167 /** 168 * Retrieves the common name for this attribute syntax. 169 * 170 * @return The common name for this attribute syntax. 171 */ 172 public String getSyntaxName() 173 { 174 return SYNTAX_UTC_TIME_NAME; 175 } 176 177 178 179 /** 180 * Retrieves the OID for this attribute syntax. 181 * 182 * @return The OID for this attribute syntax. 183 */ 184 public String getOID() 185 { 186 return SYNTAX_UTC_TIME_OID; 187 } 188 189 190 191 /** 192 * Retrieves a description for this attribute syntax. 193 * 194 * @return A description for this attribute syntax. 195 */ 196 public String getDescription() 197 { 198 return SYNTAX_UTC_TIME_DESCRIPTION; 199 } 200 201 202 203 /** 204 * Retrieves the default equality matching rule that will be used for 205 * attributes with this syntax. 206 * 207 * @return The default equality matching rule that will be used for 208 * attributes with this syntax, or <CODE>null</CODE> if equality 209 * matches will not be allowed for this type by default. 210 */ 211 public EqualityMatchingRule getEqualityMatchingRule() 212 { 213 return defaultEqualityMatchingRule; 214 } 215 216 217 218 /** 219 * Retrieves the default ordering matching rule that will be used for 220 * attributes with this syntax. 221 * 222 * @return The default ordering matching rule that will be used for 223 * attributes with this syntax, or <CODE>null</CODE> if ordering 224 * matches will not be allowed for this type by default. 225 */ 226 public OrderingMatchingRule getOrderingMatchingRule() 227 { 228 return defaultOrderingMatchingRule; 229 } 230 231 232 233 /** 234 * Retrieves the default substring matching rule that will be used for 235 * attributes with this syntax. 236 * 237 * @return The default substring matching rule that will be used for 238 * attributes with this syntax, or <CODE>null</CODE> if substring 239 * matches will not be allowed for this type by default. 240 */ 241 public SubstringMatchingRule getSubstringMatchingRule() 242 { 243 return defaultSubstringMatchingRule; 244 } 245 246 247 248 /** 249 * Retrieves the default approximate matching rule that will be used for 250 * attributes with this syntax. 251 * 252 * @return The default approximate matching rule that will be used for 253 * attributes with this syntax, or <CODE>null</CODE> if approximate 254 * matches will not be allowed for this type by default. 255 */ 256 public ApproximateMatchingRule getApproximateMatchingRule() 257 { 258 // Approximate matching will not be allowed by default. 259 return null; 260 } 261 262 263 264 /** 265 * Indicates whether the provided value is acceptable for use in an attribute 266 * with this syntax. If it is not, then the reason may be appended to the 267 * provided buffer. 268 * 269 * @param value The value for which to make the determination. 270 * @param invalidReason The buffer to which the invalid reason should be 271 * appended. 272 * 273 * @return <CODE>true</CODE> if the provided value is acceptable for use with 274 * this syntax, or <CODE>false</CODE> if not. 275 */ 276 public boolean valueIsAcceptable(ByteString value, 277 MessageBuilder invalidReason) 278 { 279 // Get the value as a string and verify that it is at least long enough for 280 // "YYYYMMDDhhmmZ", which is the shortest allowed value. 281 String valueString = value.stringValue().toUpperCase(); 282 int length = valueString.length(); 283 if (length < 11) 284 { 285 Message message = ERR_ATTR_SYNTAX_UTC_TIME_TOO_SHORT.get(valueString); 286 invalidReason.append(message); 287 return false; 288 } 289 290 291 // The first two characters are the year, and they must be numeric digits 292 // between 0 and 9. 293 for (int i=0; i < 2; i++) 294 { 295 switch (valueString.charAt(i)) 296 { 297 case '0': 298 case '1': 299 case '2': 300 case '3': 301 case '4': 302 case '5': 303 case '6': 304 case '7': 305 case '8': 306 case '9': 307 // These are all fine. 308 break; 309 default: 310 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_YEAR.get( 311 valueString, String.valueOf(valueString.charAt(i))); 312 invalidReason.append(message); 313 return false; 314 } 315 } 316 317 318 // The next two characters are the month, and they must form the string 319 // representation of an integer between 01 and 12. 320 char m1 = valueString.charAt(2); 321 char m2 = valueString.charAt(3); 322 switch (m1) 323 { 324 case '0': 325 // m2 must be a digit between 1 and 9. 326 switch (m2) 327 { 328 case '1': 329 case '2': 330 case '3': 331 case '4': 332 case '5': 333 case '6': 334 case '7': 335 case '8': 336 case '9': 337 // These are all fine. 338 break; 339 default: 340 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_MONTH.get( 341 valueString, valueString.substring(2, 4)); 342 invalidReason.append(message); 343 return false; 344 } 345 break; 346 case '1': 347 // m2 must be a digit between 0 and 2. 348 switch (m2) 349 { 350 case '0': 351 case '1': 352 case '2': 353 // These are all fine. 354 break; 355 default: 356 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_MONTH.get( 357 valueString, valueString.substring(2, 4)); 358 invalidReason.append(message); 359 return false; 360 } 361 break; 362 default: 363 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_MONTH.get( 364 valueString, valueString.substring(2, 4)); 365 invalidReason.append(message); 366 return false; 367 } 368 369 370 // The next two characters should be the day of the month, and they must 371 // form the string representation of an integer between 01 and 31. 372 // This doesn't do any validation against the year or month, so it will 373 // allow dates like April 31, or February 29 in a non-leap year, but we'll 374 // let those slide. 375 char d1 = valueString.charAt(4); 376 char d2 = valueString.charAt(5); 377 switch (d1) 378 { 379 case '0': 380 // d2 must be a digit between 1 and 9. 381 switch (d2) 382 { 383 case '1': 384 case '2': 385 case '3': 386 case '4': 387 case '5': 388 case '6': 389 case '7': 390 case '8': 391 case '9': 392 // These are all fine. 393 break; 394 default: 395 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_DAY.get( 396 valueString, valueString.substring(4, 6)); 397 invalidReason.append(message); 398 return false; 399 } 400 break; 401 case '1': 402 // Treated the same as '2'. 403 case '2': 404 // d2 must be a digit between 0 and 9. 405 switch (d2) 406 { 407 case '0': 408 case '1': 409 case '2': 410 case '3': 411 case '4': 412 case '5': 413 case '6': 414 case '7': 415 case '8': 416 case '9': 417 // These are all fine. 418 break; 419 default: 420 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_DAY.get( 421 valueString, valueString.substring(4, 6)); 422 invalidReason.append(message); 423 return false; 424 } 425 break; 426 case '3': 427 // d2 must be either 0 or 1. 428 switch (d2) 429 { 430 case '0': 431 case '1': 432 // These are all fine. 433 break; 434 default: 435 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_DAY.get( 436 valueString, valueString.substring(4, 6)); 437 invalidReason.append(message); 438 return false; 439 } 440 break; 441 default: 442 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_DAY.get(valueString, 443 valueString.substring(4, 6)); 444 invalidReason.append(message); 445 return false; 446 } 447 448 449 // The next two characters must be the hour, and they must form the string 450 // representation of an integer between 00 and 23. 451 char h1 = valueString.charAt(6); 452 char h2 = valueString.charAt(7); 453 switch (h1) 454 { 455 case '0': 456 // This is treated the same as '1'. 457 case '1': 458 switch (h2) 459 { 460 case '0': 461 case '1': 462 case '2': 463 case '3': 464 case '4': 465 case '5': 466 case '6': 467 case '7': 468 case '8': 469 case '9': 470 // These are all fine. 471 break; 472 default: 473 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_HOUR.get( 474 valueString, valueString.substring(6, 8)); 475 invalidReason.append(message); 476 return false; 477 } 478 break; 479 case '2': 480 // This must be a digit between 0 and 3. 481 switch (h2) 482 { 483 case '0': 484 case '1': 485 case '2': 486 case '3': 487 // These are all fine. 488 break; 489 default: 490 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_HOUR.get( 491 valueString, valueString.substring(6, 8)); 492 invalidReason.append(message); 493 return false; 494 } 495 break; 496 default: 497 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_HOUR.get(valueString, 498 valueString.substring(6, 8)); 499 invalidReason.append(message); 500 return false; 501 } 502 503 504 // Next, there should be two digits comprising an integer between 00 and 59 505 // for the minute. 506 m1 = valueString.charAt(8); 507 switch (m1) 508 { 509 case '0': 510 case '1': 511 case '2': 512 case '3': 513 case '4': 514 case '5': 515 // There must be at least two more characters, and the next one must 516 // be a digit between 0 and 9. 517 if (length < 11) 518 { 519 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_CHAR.get( 520 valueString, String.valueOf(m1), 8); 521 invalidReason.append(message); 522 return false; 523 } 524 525 switch (valueString.charAt(9)) 526 { 527 case '0': 528 case '1': 529 case '2': 530 case '3': 531 case '4': 532 case '5': 533 case '6': 534 case '7': 535 case '8': 536 case '9': 537 // These are all fine. 538 break; 539 default: 540 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_MINUTE.get( 541 valueString, valueString.substring(8, 10)); 542 invalidReason.append(message); 543 return false; 544 } 545 546 break; 547 548 default: 549 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_CHAR.get( 550 valueString, String.valueOf(m1), 8); 551 invalidReason.append(message); 552 return false; 553 } 554 555 556 // Next, there should be either two digits comprising an integer between 00 557 // and 60 (for the second, including a possible leap second), a letter 'Z' 558 // (for the UTC specifier), or a plus or minus sign followed by four digits 559 // (for the UTC offset). 560 char s1 = valueString.charAt(10); 561 switch (s1) 562 { 563 case '0': 564 case '1': 565 case '2': 566 case '3': 567 case '4': 568 case '5': 569 // There must be at least two more characters, and the next one must 570 // be a digit between 0 and 9. 571 if (length < 13) 572 { 573 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_CHAR.get( 574 valueString, String.valueOf(s1), 10); 575 invalidReason.append(message); 576 return false; 577 } 578 579 switch (valueString.charAt(11)) 580 { 581 case '0': 582 case '1': 583 case '2': 584 case '3': 585 case '4': 586 case '5': 587 case '6': 588 case '7': 589 case '8': 590 case '9': 591 // These are all fine. 592 break; 593 default: 594 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_SECOND.get( 595 valueString, valueString.substring(10, 12)); 596 invalidReason.append(message); 597 return false; 598 } 599 600 break; 601 case '6': 602 // There must be at least two more characters and the next one must be 603 // a 0. 604 if (length < 13) 605 { 606 607 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_CHAR.get( 608 valueString, String.valueOf(s1), 10); 609 invalidReason.append(message); 610 return false; 611 } 612 613 if (valueString.charAt(11) != '0') 614 { 615 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_SECOND.get( 616 valueString, valueString.substring(10, 12)); 617 invalidReason.append(message); 618 return false; 619 } 620 621 break; 622 case 'Z': 623 // This is fine only if we are at the end of the value. 624 if (length == 11) 625 { 626 return true; 627 } 628 else 629 { 630 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_CHAR.get( 631 valueString, String.valueOf(s1), 10); 632 invalidReason.append(message); 633 return false; 634 } 635 636 case '+': 637 case '-': 638 // These are fine only if there are exactly four more digits that 639 // specify a valid offset. 640 if (length == 15) 641 { 642 return hasValidOffset(valueString, 11, invalidReason); 643 } 644 else 645 { 646 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_CHAR.get( 647 valueString, String.valueOf(s1), 10); 648 invalidReason.append(message); 649 return false; 650 } 651 652 default: 653 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_CHAR.get( 654 valueString, String.valueOf(s1), 10); 655 invalidReason.append(message); 656 return false; 657 } 658 659 660 // The last element should be either a letter 'Z' (for the UTC specifier), 661 // or a plus or minus sign followed by four digits (for the UTC offset). 662 switch (valueString.charAt(12)) 663 { 664 case 'Z': 665 // This is fine only if we are at the end of the value. 666 if (length == 13) 667 { 668 return true; 669 } 670 else 671 { 672 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_CHAR.get( 673 valueString, String.valueOf(valueString.charAt(12)), 12); 674 invalidReason.append(message); 675 return false; 676 } 677 678 case '+': 679 case '-': 680 // These are fine only if there are four or two more digits that 681 // specify a valid offset. 682 if ((length == 17) || (length == 15)) 683 { 684 return hasValidOffset(valueString, 13, invalidReason); 685 } 686 else 687 { 688 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_CHAR.get( 689 valueString, String.valueOf(valueString.charAt(12)), 12); 690 invalidReason.append(message); 691 return false; 692 } 693 694 default: 695 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_CHAR.get( 696 valueString, String.valueOf(valueString.charAt(12)), 12); 697 invalidReason.append(message); 698 return false; 699 } 700 } 701 702 703 704 /** 705 * Indicates whether the provided string contains a valid set of two or four 706 * UTC offset digits. The provided string must have either two or four 707 * characters from the provided start position to the end of the value. 708 * 709 * @param value The whole value, including the offset. 710 * @param startPos The position of the first character that is 711 * contained in the offset. 712 * @param invalidReason The buffer to which the invalid reason may be 713 * appended if the string does not contain a valid set 714 * of UTC offset digits. 715 * 716 * @return <CODE>true</CODE> if the provided offset string is valid, or 717 * <CODE>false</CODE> if it is not. 718 */ 719 private boolean hasValidOffset(String value, int startPos, 720 MessageBuilder invalidReason) 721 { 722 int offsetLength = value.length() - startPos; 723 if (offsetLength < 2) 724 { 725 Message message = ERR_ATTR_SYNTAX_UTC_TIME_TOO_SHORT.get(value); 726 invalidReason.append(message); 727 return false; 728 } 729 730 // The first two characters must be an integer between 00 and 23. 731 switch (value.charAt(startPos)) 732 { 733 case '0': 734 case '1': 735 switch (value.charAt(startPos+1)) 736 { 737 case '0': 738 case '1': 739 case '2': 740 case '3': 741 case '4': 742 case '5': 743 case '6': 744 case '7': 745 case '8': 746 case '9': 747 // These are all fine. 748 break; 749 default: 750 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_OFFSET.get(value, 751 value.substring(startPos, 752 startPos+offsetLength)); 753 invalidReason.append(message); 754 return false; 755 } 756 break; 757 case '2': 758 switch (value.charAt(startPos+1)) 759 { 760 case '0': 761 case '1': 762 case '2': 763 case '3': 764 // These are all fine. 765 break; 766 default: 767 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_OFFSET.get(value, 768 value.substring(startPos, 769 startPos+offsetLength)); 770 invalidReason.append(message); 771 return false; 772 } 773 break; 774 default: 775 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_OFFSET.get(value, 776 value.substring(startPos, 777 startPos+offsetLength)); 778 invalidReason.append(message); 779 return false; 780 } 781 782 783 // If there are two more characters, then they must be an integer between 784 // 00 and 59. 785 if (offsetLength == 4) 786 { 787 switch (value.charAt(startPos+2)) 788 { 789 case '0': 790 case '1': 791 case '2': 792 case '3': 793 case '4': 794 case '5': 795 switch (value.charAt(startPos+3)) 796 { 797 case '0': 798 case '1': 799 case '2': 800 case '3': 801 case '4': 802 case '5': 803 case '6': 804 case '7': 805 case '8': 806 case '9': 807 // These are all fine. 808 break; 809 default: 810 Message message = 811 ERR_ATTR_SYNTAX_UTC_TIME_INVALID_OFFSET.get( 812 value,value.substring(startPos, 813 startPos+offsetLength)); 814 invalidReason.append(message); 815 return false; 816 } 817 break; 818 default: 819 Message message = ERR_ATTR_SYNTAX_UTC_TIME_INVALID_OFFSET.get(value, 820 value.substring(startPos, 821 startPos+offsetLength)); 822 invalidReason.append(message); 823 return false; 824 } 825 } 826 827 return true; 828 } 829 830 831 832 /** 833 * Retrieves an attribute value containing a UTC time representation of the 834 * provided date. 835 * 836 * @param d The date for which to retrieve the UTC time value. 837 * 838 * @return The attribute value created from the date. 839 */ 840 public static AttributeValue createUTCTimeValue(Date d) 841 { 842 String valueString; 843 844 synchronized (dateFormatLock) 845 { 846 valueString = dateFormat.format(d); 847 } 848 849 return new AttributeValue(new ASN1OctetString(valueString), 850 new ASN1OctetString(valueString)); 851 } 852 853 854 855 /** 856 * Decodes the provided normalized value as a UTC time value and 857 * retrieves a Java <CODE>Date</CODE> object containing its representation. 858 * 859 * @param normalizedValue The normalized UTC time value to decode to a 860 * Java <CODE>Date</CODE>. 861 * 862 * @return The Java <CODE>Date</CODE> created from the provided UTC time 863 * value. 864 * 865 * @throws DirectoryException If the provided value cannot be parsed as a 866 * valid UTC time string. 867 */ 868 public static Date decodeUTCTimeValue(ByteString normalizedValue) 869 throws DirectoryException 870 { 871 String valueString = normalizedValue.stringValue(); 872 try 873 { 874 synchronized (dateFormatLock) 875 { 876 return dateFormat.parse(valueString); 877 } 878 } 879 catch (Exception e) 880 { 881 if (debugEnabled()) 882 { 883 TRACER.debugCaught(DebugLogLevel.ERROR, e); 884 } 885 886 Message message = ERR_ATTR_SYNTAX_UTC_TIME_CANNOT_PARSE.get( 887 valueString, String.valueOf(e)); 888 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 889 message, e); 890 } 891 } 892 } 893