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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.server.util; 028 029import static org.opends.messages.UtilityMessages.*; 030import static org.opends.server.util.ServerConstants.*; 031 032import java.io.*; 033import java.net.InetAddress; 034import java.net.InetSocketAddress; 035import java.net.ServerSocket; 036import java.net.Socket; 037import java.nio.ByteBuffer; 038import java.nio.channels.SelectionKey; 039import java.nio.channels.Selector; 040import java.nio.channels.SocketChannel; 041import java.text.ParseException; 042import java.text.SimpleDateFormat; 043import java.util.*; 044 045import javax.naming.InitialContext; 046import javax.naming.NamingException; 047 048import org.forgerock.i18n.LocalizableMessage; 049import org.forgerock.i18n.LocalizableMessageBuilder; 050import org.forgerock.i18n.LocalizableMessageDescriptor; 051import org.forgerock.i18n.slf4j.LocalizedLogger; 052import org.forgerock.opendj.ldap.ByteSequence; 053import org.forgerock.opendj.ldap.ByteString; 054import org.forgerock.util.Reject; 055import org.opends.messages.ToolMessages; 056import org.opends.server.api.ClientConnection; 057import org.opends.server.core.DirectoryServer; 058import org.opends.server.core.ServerContext; 059import org.opends.server.types.*; 060 061import com.forgerock.opendj.cli.Argument; 062import com.forgerock.opendj.cli.ArgumentException; 063 064/** 065 * This class defines a number of static utility methods that may be used 066 * throughout the server. Note that because of the frequency with which these 067 * methods are expected to be used, very little debug logging will be performed 068 * to prevent the log from filling up with unimportant calls and to reduce the 069 * impact that debugging may have on performance. 070 */ 071@org.opends.server.types.PublicAPI( 072 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED, 073 mayInstantiate=false, 074 mayExtend=false, 075 mayInvoke=true) 076public final class StaticUtils 077{ 078 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 079 080 /** The number of bytes of a Java int. A Java int is 32 bits, i.e. 4 bytes. */ 081 public static final int INT_SIZE = 4; 082 /** The number of bytes of a Java long. A Java int is 64 bits, i.e. 8 bytes. */ 083 public static final int LONG_SIZE = 8; 084 085 /** 086 * Number of bytes in a Kibibyte. 087 * <p> 088 * Example usage: 089 * <pre> 090 * int _10KB = 10 * KB; 091 * </pre> 092 */ 093 public static final int KB = 1024; 094 /** 095 * Number of bytes in a Mebibyte. 096 * <p> 097 * Example usage: 098 * <pre> 099 * int _10MB = 10 * MB; 100 * </pre> 101 */ 102 public static final int MB = KB * KB; 103 104 /** Private constructor to prevent instantiation. */ 105 private StaticUtils() { 106 // No implementation required. 107 } 108 109 /** 110 * Construct a byte array containing the UTF-8 encoding of the 111 * provided string. This is significantly faster 112 * than calling {@link String#getBytes(String)} for ASCII strings. 113 * 114 * @param s 115 * The string to convert to a UTF-8 byte array. 116 * @return Returns a byte array containing the UTF-8 encoding of the 117 * provided string. 118 */ 119 public static byte[] getBytes(String s) 120 { 121 return com.forgerock.opendj.util.StaticUtils.getBytes(s); 122 } 123 124 125 /** 126 * Returns the provided byte array decoded as a UTF-8 string without throwing 127 * an UnsupportedEncodingException. This method is equivalent to: 128 * 129 * <pre> 130 * try 131 * { 132 * return new String(bytes, "UTF-8"); 133 * } 134 * catch (UnsupportedEncodingException e) 135 * { 136 * // Should never happen: UTF-8 is always supported. 137 * throw new RuntimeException(e); 138 * } 139 * </pre> 140 * 141 * @param bytes 142 * The byte array to be decoded as a UTF-8 string. 143 * @return The decoded string. 144 */ 145 public static String decodeUTF8(final byte[] bytes) 146 { 147 Reject.ifNull(bytes); 148 149 if (bytes.length == 0) 150 { 151 return "".intern(); 152 } 153 154 final StringBuilder builder = new StringBuilder(bytes.length); 155 final int sz = bytes.length; 156 157 for (int i = 0; i < sz; i++) 158 { 159 final byte b = bytes[i]; 160 if ((b & 0x7f) != b) 161 { 162 try 163 { 164 builder.append(new String(bytes, i, (sz - i), "UTF-8")); 165 } 166 catch (UnsupportedEncodingException e) 167 { 168 // Should never happen: UTF-8 is always supported. 169 throw new RuntimeException(e); 170 } 171 break; 172 } 173 builder.append((char) b); 174 } 175 return builder.toString(); 176 } 177 178 179 180 /** 181 * Retrieves a string representation of the provided byte in hexadecimal. 182 * 183 * @param b The byte for which to retrieve the hexadecimal string 184 * representation. 185 * @return The string representation of the provided byte in hexadecimal. 186 */ 187 188 public static String byteToHex(final byte b) 189 { 190 return com.forgerock.opendj.util.StaticUtils.byteToHex(b); 191 } 192 /** 193 * Retrieves a string representation of the provided byte in hexadecimal. 194 * 195 * @param b The byte for which to retrieve the hexadecimal string 196 * representation. 197 * @return The string representation of the provided byte in hexadecimal 198 * using lowercase characters. 199 */ 200 public static String byteToLowerHex(final byte b) 201 { 202 return com.forgerock.opendj.util.StaticUtils.byteToLowerHex(b); 203 } 204 205 /** 206 * Retrieves a string representation of the contents of the provided byte 207 * array using hexadecimal characters with no space between each byte. 208 * 209 * @param b The byte array containing the data. 210 * 211 * @return A string representation of the contents of the provided byte 212 * array using hexadecimal characters. 213 */ 214 public static String bytesToHexNoSpace(byte[] b) 215 { 216 if (b == null || b.length == 0) 217 { 218 return ""; 219 } 220 221 int arrayLength = b.length; 222 StringBuilder buffer = new StringBuilder(arrayLength * 2); 223 224 for (int i=0; i < arrayLength; i++) 225 { 226 buffer.append(byteToHex(b[i])); 227 } 228 229 return buffer.toString(); 230 } 231 232 233 234 /** 235 * Retrieves a string representation of the contents of the provided byte 236 * array using hexadecimal characters and a space between each byte. 237 * 238 * @param b The byte array containing the data. 239 * @return A string representation of the contents of the provided byte 240 * array using hexadecimal characters. 241 */ 242 public static String bytesToHex(byte[] b) 243 { 244 if (b == null || b.length == 0) 245 { 246 return ""; 247 } 248 249 int arrayLength = b.length; 250 StringBuilder buffer = new StringBuilder((arrayLength - 1) * 3 + 2); 251 buffer.append(byteToHex(b[0])); 252 253 for (int i=1; i < arrayLength; i++) 254 { 255 buffer.append(" "); 256 buffer.append(byteToHex(b[i])); 257 } 258 259 return buffer.toString(); 260 } 261 262 /** 263 * Retrieves a string representation of the contents of the provided byte 264 * sequence using hexadecimal characters and a space between each byte. 265 * 266 * @param b The byte sequence containing the data. 267 * @return A string representation of the contents of the provided byte 268 * sequence using hexadecimal characters. 269 */ 270 public static String bytesToHex(ByteSequence b) 271 { 272 if (b == null || b.length() == 0) 273 { 274 return ""; 275 } 276 277 int arrayLength = b.length(); 278 StringBuilder buffer = new StringBuilder((arrayLength - 1) * 3 + 2); 279 buffer.append(byteToHex(b.byteAt(0))); 280 281 for (int i=1; i < arrayLength; i++) 282 { 283 buffer.append(" "); 284 buffer.append(byteToHex(b.byteAt(i))); 285 } 286 287 return buffer.toString(); 288 } 289 290 291 292 /** 293 * Retrieves a string representation of the contents of the provided byte 294 * array using hexadecimal characters and a colon between each byte. 295 * 296 * @param b The byte array containing the data. 297 * 298 * @return A string representation of the contents of the provided byte 299 * array using hexadecimal characters. 300 */ 301 public static String bytesToColonDelimitedHex(byte[] b) 302 { 303 if (b == null || b.length == 0) 304 { 305 return ""; 306 } 307 308 int arrayLength = b.length; 309 StringBuilder buffer = new StringBuilder((arrayLength - 1) * 3 + 2); 310 buffer.append(byteToHex(b[0])); 311 312 for (int i=1; i < arrayLength; i++) 313 { 314 buffer.append(":"); 315 buffer.append(byteToHex(b[i])); 316 } 317 318 return buffer.toString(); 319 } 320 321 322 323 /** 324 * Retrieves a string representation of the contents of the provided byte 325 * buffer using hexadecimal characters and a space between each byte. 326 * 327 * @param b The byte buffer containing the data. 328 * 329 * @return A string representation of the contents of the provided byte 330 * buffer using hexadecimal characters. 331 */ 332 public static String bytesToHex(ByteBuffer b) 333 { 334 if (b == null) 335 { 336 return ""; 337 } 338 339 int position = b.position(); 340 int limit = b.limit(); 341 int length = limit - position; 342 343 if (length == 0) 344 { 345 return ""; 346 } 347 348 StringBuilder buffer = new StringBuilder((length - 1) * 3 + 2); 349 buffer.append(byteToHex(b.get())); 350 351 for (int i=1; i < length; i++) 352 { 353 buffer.append(" "); 354 buffer.append(byteToHex(b.get())); 355 } 356 357 b.position(position); 358 b.limit(limit); 359 360 return buffer.toString(); 361 } 362 363 364 365 /** 366 * Appends a string representation of the provided byte array to the given 367 * buffer using the specified indent. The data will be formatted with sixteen 368 * hex bytes in a row followed by the ASCII representation, then wrapping to a 369 * new line as necessary. 370 * 371 * @param buffer The buffer to which the information is to be appended. 372 * @param b The byte array containing the data to write. 373 * @param indent The number of spaces to indent the output. 374 */ 375 public static void byteArrayToHexPlusAscii(StringBuilder buffer, byte[] b, 376 int indent) 377 { 378 StringBuilder indentBuf = new StringBuilder(indent); 379 for (int i=0 ; i < indent; i++) 380 { 381 indentBuf.append(' '); 382 } 383 384 385 386 int length = b.length; 387 int pos = 0; 388 while (length - pos >= 16) 389 { 390 StringBuilder asciiBuf = new StringBuilder(17); 391 392 buffer.append(indentBuf); 393 buffer.append(byteToHex(b[pos])); 394 asciiBuf.append(byteToASCII(b[pos])); 395 pos++; 396 397 for (int i=1; i < 16; i++, pos++) 398 { 399 buffer.append(' '); 400 buffer.append(byteToHex(b[pos])); 401 asciiBuf.append(byteToASCII(b[pos])); 402 403 if (i == 7) 404 { 405 buffer.append(" "); 406 asciiBuf.append(' '); 407 } 408 } 409 410 buffer.append(" "); 411 buffer.append(asciiBuf); 412 buffer.append(EOL); 413 } 414 415 416 int remaining = length - pos; 417 if (remaining > 0) 418 { 419 StringBuilder asciiBuf = new StringBuilder(remaining+1); 420 421 buffer.append(indentBuf); 422 buffer.append(byteToHex(b[pos])); 423 asciiBuf.append(byteToASCII(b[pos])); 424 pos++; 425 426 for (int i=1; i < 16; i++) 427 { 428 buffer.append(' '); 429 430 if (i < remaining) 431 { 432 buffer.append(byteToHex(b[pos])); 433 asciiBuf.append(byteToASCII(b[pos])); 434 pos++; 435 } 436 else 437 { 438 buffer.append(" "); 439 } 440 441 if (i == 7) 442 { 443 buffer.append(" "); 444 445 if (i < remaining) 446 { 447 asciiBuf.append(' '); 448 } 449 } 450 } 451 452 buffer.append(" "); 453 buffer.append(asciiBuf); 454 buffer.append(EOL); 455 } 456 } 457 458 private static char byteToASCII(byte b) 459 { 460 return com.forgerock.opendj.util.StaticUtils.byteToASCII(b); 461 } 462 463 /** 464 * Appends a string representation of the remaining unread data in the 465 * provided byte buffer to the given buffer using the specified indent. 466 * The data will be formatted with sixteen hex bytes in a row followed by 467 * the ASCII representation, then wrapping to a new line as necessary. 468 * The state of the byte buffer is not changed. 469 * 470 * @param buffer The buffer to which the information is to be appended. 471 * @param b The byte buffer containing the data to write. 472 * The data from the position to the limit is written. 473 * @param indent The number of spaces to indent the output. 474 */ 475 public static void byteArrayToHexPlusAscii(StringBuilder buffer, ByteBuffer b, 476 int indent) 477 { 478 StringBuilder indentBuf = new StringBuilder(indent); 479 for (int i=0 ; i < indent; i++) 480 { 481 indentBuf.append(' '); 482 } 483 484 485 int position = b.position(); 486 int limit = b.limit(); 487 int length = limit - position; 488 int pos = 0; 489 while (length - pos >= 16) 490 { 491 StringBuilder asciiBuf = new StringBuilder(17); 492 493 byte currentByte = b.get(); 494 buffer.append(indentBuf); 495 buffer.append(byteToHex(currentByte)); 496 asciiBuf.append(byteToASCII(currentByte)); 497 pos++; 498 499 for (int i=1; i < 16; i++, pos++) 500 { 501 currentByte = b.get(); 502 buffer.append(' '); 503 buffer.append(byteToHex(currentByte)); 504 asciiBuf.append(byteToASCII(currentByte)); 505 506 if (i == 7) 507 { 508 buffer.append(" "); 509 asciiBuf.append(' '); 510 } 511 } 512 513 buffer.append(" "); 514 buffer.append(asciiBuf); 515 buffer.append(EOL); 516 } 517 518 519 int remaining = length - pos; 520 if (remaining > 0) 521 { 522 StringBuilder asciiBuf = new StringBuilder(remaining+1); 523 524 byte currentByte = b.get(); 525 buffer.append(indentBuf); 526 buffer.append(byteToHex(currentByte)); 527 asciiBuf.append(byteToASCII(currentByte)); 528 529 for (int i=1; i < 16; i++) 530 { 531 buffer.append(' '); 532 533 if (i < remaining) 534 { 535 currentByte = b.get(); 536 buffer.append(byteToHex(currentByte)); 537 asciiBuf.append(byteToASCII(currentByte)); 538 } 539 else 540 { 541 buffer.append(" "); 542 } 543 544 if (i == 7) 545 { 546 buffer.append(" "); 547 548 if (i < remaining) 549 { 550 asciiBuf.append(' '); 551 } 552 } 553 } 554 555 buffer.append(" "); 556 buffer.append(asciiBuf); 557 buffer.append(EOL); 558 } 559 560 b.position(position); 561 b.limit(limit); 562 } 563 564 565 566 /** 567 * Compare two byte arrays for order. Returns a negative integer, 568 * zero, or a positive integer as the first argument is less than, 569 * equal to, or greater than the second. 570 * 571 * @param a 572 * The first byte array to be compared. 573 * @param a2 574 * The second byte array to be compared. 575 * @return Returns a negative integer, zero, or a positive integer 576 * if the first byte array is less than, equal to, or greater 577 * than the second. 578 */ 579 public static int compare(byte[] a, byte[] a2) { 580 if (a == a2) { 581 return 0; 582 } 583 584 if (a == null) { 585 return -1; 586 } 587 588 if (a2 == null) { 589 return 1; 590 } 591 592 int minLength = Math.min(a.length, a2.length); 593 for (int i = 0; i < minLength; i++) { 594 int firstByte = 0xFF & a[i]; 595 int secondByte = 0xFF & a2[i]; 596 if (firstByte != secondByte) { 597 if (firstByte < secondByte) { 598 return -1; 599 } else if (firstByte > secondByte) { 600 return 1; 601 } 602 } 603 } 604 605 return a.length - a2.length; 606 } 607 608 609 610 /** 611 * Indicates whether the two array lists are equal. They will be 612 * considered equal if they have the same number of elements, and 613 * the corresponding elements between them are equal (in the same 614 * order). 615 * 616 * @param list1 617 * The first list for which to make the determination. 618 * @param list2 619 * The second list for which to make the determination. 620 * @return {@code true} if the two array lists are equal, or 621 * {@code false} if they are not. 622 */ 623 public static boolean listsAreEqual(List<?> list1, List<?> list2) 624 { 625 if (list1 == null) 626 { 627 return list2 == null; 628 } 629 else if (list2 == null) 630 { 631 return false; 632 } 633 634 int numElements = list1.size(); 635 if (numElements != list2.size()) 636 { 637 return false; 638 } 639 640 // If either of the lists doesn't support random access, then fall back 641 // on their equals methods and go ahead and create some garbage with the 642 // iterators. 643 if (!(list1 instanceof RandomAccess) || 644 !(list2 instanceof RandomAccess)) 645 { 646 return list1.equals(list2); 647 } 648 649 // Otherwise we can just retrieve the elements efficiently via their index. 650 for (int i=0; i < numElements; i++) 651 { 652 Object o1 = list1.get(i); 653 Object o2 = list2.get(i); 654 655 if (o1 == null) 656 { 657 if (o2 != null) 658 { 659 return false; 660 } 661 } 662 else if (! o1.equals(o2)) 663 { 664 return false; 665 } 666 } 667 668 return true; 669 } 670 671 /** 672 * Retrieves the best human-readable message for the provided exception. For 673 * exceptions defined in the OpenDJ project, it will attempt to use the 674 * message (combining it with the message ID if available). For some 675 * exceptions that use encapsulation (e.g., InvocationTargetException), it 676 * will be unwrapped and the cause will be treated. For all others, the 677 * 678 * 679 * @param t The {@code Throwable} object for which to retrieve the message. 680 * 681 * @return The human-readable message generated for the provided exception. 682 */ 683 public static LocalizableMessage getExceptionMessage(Throwable t) 684 { 685 if (t instanceof IdentifiedException) 686 { 687 IdentifiedException ie = (IdentifiedException) t; 688 689 StringBuilder message = new StringBuilder(); 690 message.append(ie.getMessage()); 691 message.append(" (id="); 692 LocalizableMessage ieMsg = ie.getMessageObject(); 693 if (ieMsg != null) { 694 message.append(ieMsg.resourceName()).append("-").append(ieMsg.ordinal()); 695 } else { 696 message.append("-1"); 697 } 698 message.append(")"); 699 return LocalizableMessage.raw(message.toString()); 700 } 701 else 702 { 703 return com.forgerock.opendj.util.StaticUtils.getExceptionMessage(t); 704 } 705 } 706 707 708 709 /** 710 * Retrieves a stack trace from the provided exception as a single-line 711 * string. 712 * 713 * @param t The exception for which to retrieve the stack trace. 714 * 715 * @return A stack trace from the provided exception as a single-line string. 716 */ 717 public static String stackTraceToSingleLineString(Throwable t) 718 { 719 return com.forgerock.opendj.util.StaticUtils.stackTraceToSingleLineString(t, DynamicConstants.DEBUG_BUILD); 720 } 721 722 723 724 /** 725 * Appends a single-line string representation of the provided exception to 726 * the given buffer. 727 * 728 * @param buffer The buffer to which the information is to be appended. 729 * @param t The exception for which to retrieve the stack trace. 730 */ 731 public static void stackTraceToSingleLineString(StringBuilder buffer, 732 Throwable t) 733 { 734 com.forgerock.opendj.util.StaticUtils.stackTraceToSingleLineString(buffer, t, DynamicConstants.DEBUG_BUILD); 735 } 736 737 738 739 /** 740 * Retrieves a string representation of the stack trace for the provided 741 * exception. 742 * 743 * @param t The exception for which to retrieve the stack trace. 744 * 745 * @return A string representation of the stack trace for the provided 746 * exception. 747 */ 748 public static String stackTraceToString(Throwable t) 749 { 750 StringBuilder buffer = new StringBuilder(); 751 stackTraceToString(buffer, t); 752 return buffer.toString(); 753 } 754 755 /** 756 * Check if the stack trace of provided exception contains a given cause. 757 * 758 * @param throwable 759 * exception that may contain the cause 760 * @param searchedCause 761 * class of the cause to look for. Any subclass will match. 762 * @return true if and only if the given cause is found as a cause of any 763 * level in the provided exception. 764 */ 765 public static boolean stackTraceContainsCause( 766 Throwable throwable, Class<? extends Throwable> searchedCause) 767 { 768 Throwable t = throwable; 769 while ((t = t.getCause()) != null) 770 { 771 if (searchedCause.isAssignableFrom(t.getClass())) 772 { 773 return true; 774 } 775 776 } 777 return false; 778 } 779 780 /** 781 * Appends a string representation of the stack trace for the provided 782 * exception to the given buffer. 783 * 784 * @param buffer The buffer to which the information is to be appended. 785 * @param t The exception for which to retrieve the stack trace. 786 */ 787 public static void stackTraceToString(StringBuilder buffer, Throwable t) 788 { 789 if (t == null) 790 { 791 return; 792 } 793 794 buffer.append(t); 795 796 for (StackTraceElement e : t.getStackTrace()) 797 { 798 buffer.append(EOL); 799 buffer.append(" "); 800 buffer.append(e.getClassName()); 801 buffer.append("."); 802 buffer.append(e.getMethodName()); 803 buffer.append("("); 804 buffer.append(e.getFileName()); 805 buffer.append(":"); 806 buffer.append(e.getLineNumber()); 807 buffer.append(")"); 808 } 809 810 while (t.getCause() != null) 811 { 812 t = t.getCause(); 813 buffer.append(EOL); 814 buffer.append("Caused by "); 815 buffer.append(t); 816 817 for (StackTraceElement e : t.getStackTrace()) 818 { 819 buffer.append(EOL); 820 buffer.append(" "); 821 buffer.append(e.getClassName()); 822 buffer.append("."); 823 buffer.append(e.getMethodName()); 824 buffer.append("("); 825 buffer.append(e.getFileName()); 826 buffer.append(":"); 827 buffer.append(e.getLineNumber()); 828 buffer.append(")"); 829 } 830 } 831 832 buffer.append(EOL); 833 } 834 835 836 837 /** 838 * Retrieves a backtrace for the current thread consisting only of filenames 839 * and line numbers that may be useful in debugging the origin of problems 840 * that should not have happened. Note that this may be an expensive 841 * operation to perform, so it should only be used for error conditions or 842 * debugging. 843 * 844 * @return A backtrace for the current thread. 845 */ 846 public static String getBacktrace() 847 { 848 StringBuilder buffer = new StringBuilder(); 849 850 StackTraceElement[] elements = Thread.currentThread().getStackTrace(); 851 852 if (elements.length > 1) 853 { 854 buffer.append(elements[1].getFileName()); 855 buffer.append(":"); 856 buffer.append(elements[1].getLineNumber()); 857 858 for (int i=2; i < elements.length; i++) 859 { 860 buffer.append(" "); 861 buffer.append(elements[i].getFileName()); 862 buffer.append(":"); 863 buffer.append(elements[i].getLineNumber()); 864 } 865 } 866 867 return buffer.toString(); 868 } 869 870 871 872 /** 873 * Retrieves a backtrace for the provided exception consisting of only 874 * filenames and line numbers that may be useful in debugging the origin of 875 * problems. This is less expensive than the call to 876 * {@code getBacktrace} without any arguments if an exception has already 877 * been thrown. 878 * 879 * @param t The exception for which to obtain the backtrace. 880 * 881 * @return A backtrace from the provided exception. 882 */ 883 public static String getBacktrace(Throwable t) 884 { 885 StringBuilder buffer = new StringBuilder(); 886 887 StackTraceElement[] elements = t.getStackTrace(); 888 889 if (elements.length > 0) 890 { 891 buffer.append(elements[0].getFileName()); 892 buffer.append(":"); 893 buffer.append(elements[0].getLineNumber()); 894 895 for (int i=1; i < elements.length; i++) 896 { 897 buffer.append(" "); 898 buffer.append(elements[i].getFileName()); 899 buffer.append(":"); 900 buffer.append(elements[i].getLineNumber()); 901 } 902 } 903 904 return buffer.toString(); 905 } 906 907 908 909 /** 910 * Indicates whether the provided character is a numeric digit. 911 * 912 * @param c The character for which to make the determination. 913 * 914 * @return {@code true} if the provided character represents a numeric 915 * digit, or {@code false} if not. 916 */ 917 public static boolean isDigit(final char c) { 918 return com.forgerock.opendj.util.StaticUtils.isDigit(c); 919 } 920 921 922 923 /** 924 * Indicates whether the provided character is an ASCII alphabetic character. 925 * 926 * @param c The character for which to make the determination. 927 * 928 * @return {@code true} if the provided value is an uppercase or 929 * lowercase ASCII alphabetic character, or {@code false} if it 930 * is not. 931 */ 932 public static boolean isAlpha(final char c) { 933 return com.forgerock.opendj.util.StaticUtils.isAlpha(c); 934 } 935 936 /** 937 * Indicates whether the provided character is a hexadecimal digit. 938 * 939 * @param c The character for which to make the determination. 940 * 941 * @return {@code true} if the provided character represents a 942 * hexadecimal digit, or {@code false} if not. 943 */ 944 public static boolean isHexDigit(final char c) { 945 return com.forgerock.opendj.util.StaticUtils.isHexDigit(c); 946 } 947 948 /** 949 * Indicates whether the provided byte represents a hexadecimal digit. 950 * 951 * @param b The byte for which to make the determination. 952 * 953 * @return {@code true} if the provided byte represents a hexadecimal 954 * digit, or {@code false} if not. 955 */ 956 public static boolean isHexDigit(byte b) 957 { 958 switch (b) 959 { 960 case '0': 961 case '1': 962 case '2': 963 case '3': 964 case '4': 965 case '5': 966 case '6': 967 case '7': 968 case '8': 969 case '9': 970 case 'A': 971 case 'B': 972 case 'C': 973 case 'D': 974 case 'E': 975 case 'F': 976 case 'a': 977 case 'b': 978 case 'c': 979 case 'd': 980 case 'e': 981 case 'f': 982 return true; 983 default: 984 return false; 985 } 986 } 987 988 989 990 /** 991 * Converts the provided hexadecimal string to a byte array. 992 * 993 * @param hexString The hexadecimal string to convert to a byte array. 994 * 995 * @return The byte array containing the binary representation of the 996 * provided hex string. 997 * 998 * @throws ParseException If the provided string contains invalid 999 * hexadecimal digits or does not contain an even 1000 * number of digits. 1001 */ 1002 public static byte[] hexStringToByteArray(String hexString) 1003 throws ParseException 1004 { 1005 int length; 1006 if (hexString == null || ((length = hexString.length()) == 0)) 1007 { 1008 return new byte[0]; 1009 } 1010 1011 1012 if ((length % 2) == 1) 1013 { 1014 LocalizableMessage message = ERR_HEX_DECODE_INVALID_LENGTH.get(hexString); 1015 throw new ParseException(message.toString(), 0); 1016 } 1017 1018 1019 int pos = 0; 1020 int arrayLength = length / 2; 1021 byte[] returnArray = new byte[arrayLength]; 1022 for (int i=0; i < arrayLength; i++) 1023 { 1024 switch (hexString.charAt(pos++)) 1025 { 1026 case '0': 1027 returnArray[i] = 0x00; 1028 break; 1029 case '1': 1030 returnArray[i] = 0x10; 1031 break; 1032 case '2': 1033 returnArray[i] = 0x20; 1034 break; 1035 case '3': 1036 returnArray[i] = 0x30; 1037 break; 1038 case '4': 1039 returnArray[i] = 0x40; 1040 break; 1041 case '5': 1042 returnArray[i] = 0x50; 1043 break; 1044 case '6': 1045 returnArray[i] = 0x60; 1046 break; 1047 case '7': 1048 returnArray[i] = 0x70; 1049 break; 1050 case '8': 1051 returnArray[i] = (byte) 0x80; 1052 break; 1053 case '9': 1054 returnArray[i] = (byte) 0x90; 1055 break; 1056 case 'A': 1057 case 'a': 1058 returnArray[i] = (byte) 0xA0; 1059 break; 1060 case 'B': 1061 case 'b': 1062 returnArray[i] = (byte) 0xB0; 1063 break; 1064 case 'C': 1065 case 'c': 1066 returnArray[i] = (byte) 0xC0; 1067 break; 1068 case 'D': 1069 case 'd': 1070 returnArray[i] = (byte) 0xD0; 1071 break; 1072 case 'E': 1073 case 'e': 1074 returnArray[i] = (byte) 0xE0; 1075 break; 1076 case 'F': 1077 case 'f': 1078 returnArray[i] = (byte) 0xF0; 1079 break; 1080 default: 1081 LocalizableMessage message = ERR_HEX_DECODE_INVALID_CHARACTER.get( 1082 hexString, hexString.charAt(pos-1)); 1083 throw new ParseException(message.toString(), 0); 1084 } 1085 1086 switch (hexString.charAt(pos++)) 1087 { 1088 case '0': 1089 // No action required. 1090 break; 1091 case '1': 1092 returnArray[i] |= 0x01; 1093 break; 1094 case '2': 1095 returnArray[i] |= 0x02; 1096 break; 1097 case '3': 1098 returnArray[i] |= 0x03; 1099 break; 1100 case '4': 1101 returnArray[i] |= 0x04; 1102 break; 1103 case '5': 1104 returnArray[i] |= 0x05; 1105 break; 1106 case '6': 1107 returnArray[i] |= 0x06; 1108 break; 1109 case '7': 1110 returnArray[i] |= 0x07; 1111 break; 1112 case '8': 1113 returnArray[i] |= 0x08; 1114 break; 1115 case '9': 1116 returnArray[i] |= 0x09; 1117 break; 1118 case 'A': 1119 case 'a': 1120 returnArray[i] |= 0x0A; 1121 break; 1122 case 'B': 1123 case 'b': 1124 returnArray[i] |= 0x0B; 1125 break; 1126 case 'C': 1127 case 'c': 1128 returnArray[i] |= 0x0C; 1129 break; 1130 case 'D': 1131 case 'd': 1132 returnArray[i] |= 0x0D; 1133 break; 1134 case 'E': 1135 case 'e': 1136 returnArray[i] |= 0x0E; 1137 break; 1138 case 'F': 1139 case 'f': 1140 returnArray[i] |= 0x0F; 1141 break; 1142 default: 1143 LocalizableMessage message = ERR_HEX_DECODE_INVALID_CHARACTER.get( 1144 hexString, hexString.charAt(pos-1)); 1145 throw new ParseException(message.toString(), 0); 1146 } 1147 } 1148 1149 return returnArray; 1150 } 1151 1152 1153 1154 /** 1155 * Indicates whether the provided value needs to be base64-encoded if it is 1156 * represented in LDIF form. 1157 * 1158 * @param valueBytes The binary representation of the attribute value for 1159 * which to make the determination. 1160 * 1161 * @return {@code true} if the value needs to be base64-encoded if it is 1162 * represented in LDIF form, or {@code false} if not. 1163 */ 1164 public static boolean needsBase64Encoding(ByteSequence valueBytes) 1165 { 1166 int length; 1167 if (valueBytes == null || ((length = valueBytes.length()) == 0)) 1168 { 1169 return false; 1170 } 1171 1172 1173 // If the value starts with a space, colon, or less than, then it needs to 1174 // be base64-encoded. 1175 switch (valueBytes.byteAt(0)) 1176 { 1177 case 0x20: // Space 1178 case 0x3A: // Colon 1179 case 0x3C: // Less-than 1180 return true; 1181 } 1182 1183 1184 // If the value ends with a space, then it needs to be base64-encoded. 1185 if (length > 1 && valueBytes.byteAt(length - 1) == 0x20) 1186 { 1187 return true; 1188 } 1189 1190 1191 // If the value contains a null, newline, or return character, then it needs 1192 // to be base64-encoded. 1193 byte b; 1194 for (int i = 0; i < valueBytes.length(); i++) 1195 { 1196 b = valueBytes.byteAt(i); 1197 if (b < 0 || 127 < b) 1198 { 1199 return true; 1200 } 1201 1202 switch (b) 1203 { 1204 case 0x00: // Null 1205 case 0x0A: // New line 1206 case 0x0D: // Carriage return 1207 return true; 1208 } 1209 } 1210 1211 1212 // If we've made it here, then there's no reason to base64-encode. 1213 return false; 1214 } 1215 1216 1217 1218 /** 1219 * Indicates whether the provided value needs to be base64-encoded if it is 1220 * represented in LDIF form. 1221 * 1222 * @param valueString The string representation of the attribute value for 1223 * which to make the determination. 1224 * 1225 * @return {@code true} if the value needs to be base64-encoded if it is 1226 * represented in LDIF form, or {@code false} if not. 1227 */ 1228 public static boolean needsBase64Encoding(String valueString) 1229 { 1230 int length; 1231 if (valueString == null || ((length = valueString.length()) == 0)) 1232 { 1233 return false; 1234 } 1235 1236 1237 // If the value starts with a space, colon, or less than, then it needs to 1238 // be base64-encoded. 1239 switch (valueString.charAt(0)) 1240 { 1241 case ' ': 1242 case ':': 1243 case '<': 1244 return true; 1245 } 1246 1247 1248 // If the value ends with a space, then it needs to be base64-encoded. 1249 if (length > 1 && valueString.charAt(length - 1) == ' ') 1250 { 1251 return true; 1252 } 1253 1254 1255 // If the value contains a null, newline, or return character, then it needs 1256 // to be base64-encoded. 1257 for (int i=0; i < length; i++) 1258 { 1259 char c = valueString.charAt(i); 1260 if (c <= 0 || c == 0x0A || c == 0x0D || c > 127) 1261 { 1262 return true; 1263 } 1264 } 1265 1266 1267 // If we've made it here, then there's no reason to base64-encode. 1268 return false; 1269 } 1270 1271 1272 1273 /** 1274 * Indicates whether the use of the exec method will be allowed on this 1275 * system. It will be allowed by default, but that capability will be removed 1276 * if the org.opends.server.DisableExec system property is set and has any 1277 * value other than "false", "off", "no", or "0". 1278 * 1279 * @return {@code true} if the use of the exec method should be allowed, 1280 * or {@code false} if it should not be allowed. 1281 */ 1282 public static boolean mayUseExec() 1283 { 1284 return !DirectoryServer.getEnvironmentConfig().disableExec(); 1285 } 1286 1287 1288 1289 /** 1290 * Executes the specified command on the system and captures its output. This 1291 * will not return until the specified process has completed. 1292 * 1293 * @param command The command to execute. 1294 * @param args The set of arguments to provide to the command. 1295 * @param workingDirectory The working directory to use for the command, or 1296 * {@code null} if the default directory 1297 * should be used. 1298 * @param environment The set of environment variables that should be 1299 * set when executing the command, or 1300 * {@code null} if none are needed. 1301 * @param output The output generated by the command while it was 1302 * running. This will include both standard 1303 * output and standard error. It may be 1304 * {@code null} if the output does not need to 1305 * be captured. 1306 * 1307 * @return The exit code for the command. 1308 * 1309 * @throws IOException If an I/O problem occurs while trying to execute the 1310 * command. 1311 * 1312 * @throws SecurityException If the security policy will not allow the 1313 * command to be executed. 1314 * 1315 * @throws InterruptedException If the current thread is interrupted by 1316 * another thread while it is waiting, then 1317 * the wait is ended and an InterruptedException 1318 * is thrown. 1319 */ 1320 public static int exec(String command, String[] args, File workingDirectory, 1321 Map<String,String> environment, List<String> output) 1322 throws IOException, SecurityException, InterruptedException 1323 { 1324 // See whether we'll allow the use of exec on this system. If not, then 1325 // throw an exception. 1326 if (! mayUseExec()) 1327 { 1328 throw new SecurityException(ERR_EXEC_DISABLED.get(command).toString()); 1329 } 1330 1331 1332 ArrayList<String> commandAndArgs = new ArrayList<>(); 1333 commandAndArgs.add(command); 1334 if (args != null && args.length > 0) 1335 { 1336 Collections.addAll(commandAndArgs, args); 1337 } 1338 1339 ProcessBuilder processBuilder = new ProcessBuilder(commandAndArgs); 1340 processBuilder.redirectErrorStream(true); 1341 1342 if (workingDirectory != null && workingDirectory.isDirectory()) 1343 { 1344 processBuilder.directory(workingDirectory); 1345 } 1346 1347 if (environment != null && !environment.isEmpty()) 1348 { 1349 processBuilder.environment().putAll(environment); 1350 } 1351 1352 Process process = processBuilder.start(); 1353 1354 // We must exhaust stdout and stderr before calling waitfor. Since we 1355 // redirected the error stream, we just have to read from stdout. 1356 InputStream processStream = process.getInputStream(); 1357 BufferedReader reader = 1358 new BufferedReader(new InputStreamReader(processStream)); 1359 String line = null; 1360 1361 try 1362 { 1363 while((line = reader.readLine()) != null) 1364 { 1365 if(output != null) 1366 { 1367 output.add(line); 1368 } 1369 } 1370 } 1371 catch(IOException ioe) 1372 { 1373 // If this happens, then we have no choice but to forcefully terminate 1374 // the process. 1375 try 1376 { 1377 process.destroy(); 1378 } 1379 catch (Exception e) 1380 { 1381 logger.traceException(e); 1382 } 1383 1384 throw ioe; 1385 } 1386 finally 1387 { 1388 try 1389 { 1390 reader.close(); 1391 } 1392 catch(IOException e) 1393 { 1394 logger.traceException(e); 1395 } 1396 } 1397 1398 return process.waitFor(); 1399 } 1400 1401 1402 1403 /** 1404 * Indicates whether the provided string contains a name or OID for a schema 1405 * element like an attribute type or objectclass. 1406 * 1407 * @param element The string containing the substring for which to 1408 * make the determination. 1409 * @param startPos The position of the first character that is to be 1410 * checked. 1411 * @param endPos The position of the first character after the start 1412 * position that is not to be checked. 1413 * @param invalidReason The buffer to which the invalid reason is to be 1414 * appended if a problem is found. 1415 * 1416 * @return {@code true} if the provided string contains a valid name or 1417 * OID for a schema element, or {@code false} if it does not. 1418 */ 1419 public static boolean isValidSchemaElement(String element, int startPos, 1420 int endPos, 1421 LocalizableMessageBuilder invalidReason) 1422 { 1423 if (element == null || startPos >= endPos) 1424 { 1425 invalidReason.append(ERR_SCHEMANAME_EMPTY_VALUE.get()); 1426 return false; 1427 } 1428 1429 1430 char c = element.charAt(startPos); 1431 if (isAlpha(c)) 1432 { 1433 // This can only be a name and not an OID. The only remaining characters 1434 // must be letters, digits, dashes, and possibly the underscore. 1435 for (int i=startPos+1; i < endPos; i++) 1436 { 1437 c = element.charAt(i); 1438 if (!isAlpha(c) 1439 && !isDigit(c) 1440 && c != '-' 1441 && (c != '_' || !DirectoryServer.allowAttributeNameExceptions())) 1442 { 1443 // This is an illegal character for an attribute name. 1444 invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(element, c, i)); 1445 return false; 1446 } 1447 } 1448 } 1449 else if (isDigit(c)) 1450 { 1451 // This should indicate an OID, but it may also be a name if name 1452 // exceptions are enabled. Since we don't know for sure, we'll just 1453 // hold off until we know for sure. 1454 boolean isKnown = !DirectoryServer.allowAttributeNameExceptions(); 1455 boolean isNumeric = true; 1456 boolean lastWasDot = false; 1457 1458 for (int i=startPos+1; i < endPos; i++) 1459 { 1460 c = element.charAt(i); 1461 if (c == '.') 1462 { 1463 if (isKnown) 1464 { 1465 if (isNumeric) 1466 { 1467 // This is probably legal unless the last character was also a 1468 // period. 1469 if (lastWasDot) 1470 { 1471 invalidReason.append(ERR_SCHEMANAME_CONSECUTIVE_PERIODS.get( 1472 element, i)); 1473 return false; 1474 } 1475 else 1476 { 1477 lastWasDot = true; 1478 } 1479 } 1480 else 1481 { 1482 // This is an illegal character. 1483 invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get( 1484 element, c, i)); 1485 return false; 1486 } 1487 } 1488 else 1489 { 1490 // Now we know that this must be a numeric OID and not an attribute 1491 // name with exceptions allowed. 1492 lastWasDot = true; 1493 isKnown = true; 1494 isNumeric = true; 1495 } 1496 } 1497 else 1498 { 1499 lastWasDot = false; 1500 1501 if (isAlpha(c) || c == '-' || c == '_') 1502 { 1503 if (isKnown) 1504 { 1505 if (isNumeric) 1506 { 1507 // This is an illegal character for a numeric OID. 1508 invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get( 1509 element, c, i)); 1510 return false; 1511 } 1512 } 1513 else 1514 { 1515 // Now we know that this must be an attribute name with exceptions 1516 // allowed and not a numeric OID. 1517 isKnown = true; 1518 isNumeric = false; 1519 } 1520 } 1521 else if (! isDigit(c)) 1522 { 1523 // This is an illegal character. 1524 invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get( 1525 element, c, i)); 1526 return false; 1527 } 1528 } 1529 } 1530 } 1531 else 1532 { 1533 // This is an illegal character. 1534 invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get( 1535 element, c, startPos)); 1536 return false; 1537 } 1538 1539 1540 // If we've gotten here, then the value is fine. 1541 return true; 1542 } 1543 1544 1545 1546 /** 1547 * Indicates whether the provided TCP address is already in use. 1548 * 1549 * @param address IP address of the TCP address for which to make 1550 * the determination. 1551 * @param port TCP port number of the TCP address for which to 1552 * make the determination. 1553 * @param allowReuse Whether or not TCP address reuse is allowed when 1554 * making the determination. 1555 * 1556 * @return {@code true} if the provided TCP address is already in 1557 * use, or {@code false} otherwise. 1558 */ 1559 public static boolean isAddressInUse( 1560 InetAddress address, int port, 1561 boolean allowReuse) 1562 { 1563 // Return pessimistic. 1564 boolean isInUse = true; 1565 Socket clientSocket = null; 1566 ServerSocket serverSocket = null; 1567 try { 1568 // HACK: 1569 // With dual stacks we can have a situation when INADDR_ANY/PORT 1570 // is bound in TCP4 space but available in TCP6 space and since 1571 // JavaServerSocket implemantation will always use TCP46 on dual 1572 // stacks the bind below will always succeed in such cases thus 1573 // shadowing anything that is already bound to INADDR_ANY/PORT. 1574 // While technically correct, with IPv4 and IPv6 being separate 1575 // address spaces, it presents a problem to end users because a 1576 // common case scenario is to have a single service serving both 1577 // address spaces ie listening to the same port in both spaces 1578 // on wildcard addresses 0 and ::. ServerSocket implemantation 1579 // does not provide any means of working with each address space 1580 // separately such as doing TCP4 or TCP6 only binds thus we have 1581 // to do a dummy connect to INADDR_ANY/PORT to check if it is 1582 // bound to something already. This is only needed for wildcard 1583 // addresses as specific IPv4 or IPv6 addresses will always be 1584 // handled in their respective address space. 1585 if (address.isAnyLocalAddress()) { 1586 clientSocket = new Socket(); 1587 try { 1588 // This might fail on some stacks but this is the best we 1589 // can do. No need for explicit timeout since it is local 1590 // address and we have to know for sure unless it fails. 1591 clientSocket.connect(new InetSocketAddress(address, port)); 1592 } catch (IOException e) { 1593 // Expected, ignore. 1594 } 1595 if (clientSocket.isConnected()) { 1596 return true; 1597 } 1598 } 1599 serverSocket = new ServerSocket(); 1600 serverSocket.setReuseAddress(allowReuse); 1601 serverSocket.bind(new InetSocketAddress(address, port)); 1602 isInUse = false; 1603 } catch (IOException e) { 1604 isInUse = true; 1605 } finally { 1606 try { 1607 if (serverSocket != null) { 1608 serverSocket.close(); 1609 } 1610 } catch (Exception e) {} 1611 try { 1612 if (clientSocket != null) { 1613 clientSocket.close(); 1614 } 1615 } catch (Exception e) {} 1616 } 1617 return isInUse; 1618 } 1619 1620 1621 1622 /** 1623 * Returns a lower-case string representation of a given string, verifying for null input string. 1624 * {@see com.forgerock.opendj.util.StaticUtils#toLowerCase(String s)} 1625 * 1626 * @param s the mixed case string 1627 * @return a lower-case string 1628 */ 1629 public static String toLowerCase(String s) 1630 { 1631 return (s == null ? null : com.forgerock.opendj.util.StaticUtils.toLowerCase(s)); 1632 } 1633 1634 /** 1635 * Appends a lower-case string representation of a given ByteSequence to a StringBuilder, 1636 * verifying for null input. 1637 * {@see com.forgerock.opendj.util.StaticUtils#toLowerCase(ByteSequence s, StringBuilder string)} 1638 * 1639 * @param b The byte array for which to obtain the lowercase string 1640 * representation. 1641 * @param buffer The buffer to which the lowercase form of the string should 1642 * be appended. 1643 * @param trim Indicates whether leading and trailing spaces should be 1644 * omitted from the string representation. 1645 */ 1646 public static void toLowerCase(ByteSequence b, StringBuilder buffer, boolean trim) 1647 { 1648 if (b == null) 1649 { 1650 return; 1651 } 1652 1653 if (trim) 1654 { 1655 int begin = 0; 1656 int end = b.length() - 1; 1657 while (begin <= end) 1658 { 1659 if (b.byteAt(begin) == ' ') 1660 { 1661 begin++; 1662 } 1663 else if (b.byteAt(end) == ' ') 1664 { 1665 end--; 1666 } 1667 else 1668 { 1669 break; 1670 } 1671 } 1672 if (begin > 0 || end < b.length() - 1) 1673 { 1674 b = b.subSequence(begin, end + 1); 1675 } 1676 } 1677 1678 com.forgerock.opendj.util.StaticUtils.toLowerCase(b, buffer); 1679 } 1680 1681 1682 1683 /** 1684 * Retrieves an uppercase representation of the given string. This 1685 * implementation presumes that the provided string will contain only ASCII 1686 * characters and is optimized for that case. However, if a non-ASCII 1687 * character is encountered it will fall back on a more expensive algorithm 1688 * that will work properly for non-ASCII characters. 1689 * 1690 * @param s The string for which to obtain the uppercase representation. 1691 * 1692 * @return The uppercase representation of the given string. 1693 */ 1694 public static String toUpperCase(String s) 1695 { 1696 if (s == null) 1697 { 1698 return null; 1699 } 1700 1701 StringBuilder buffer = new StringBuilder(s.length()); 1702 toUpperCase(s, buffer); 1703 return buffer.toString(); 1704 } 1705 1706 1707 1708 /** 1709 * Appends an uppercase representation of the given string to the provided 1710 * buffer. This implementation presumes that the provided string will contain 1711 * only ASCII characters and is optimized for that case. However, if a 1712 * non-ASCII character is encountered it will fall back on a more expensive 1713 * algorithm that will work properly for non-ASCII characters. 1714 * 1715 * @param s The string for which to obtain the uppercase 1716 * representation. 1717 * @param buffer The buffer to which the uppercase form of the string should 1718 * be appended. 1719 */ 1720 public static void toUpperCase(String s, StringBuilder buffer) 1721 { 1722 if (s == null) 1723 { 1724 return; 1725 } 1726 1727 int length = s.length(); 1728 for (int i=0; i < length; i++) 1729 { 1730 char c = s.charAt(i); 1731 1732 if ((c & 0x7F) != c) 1733 { 1734 buffer.append(s.substring(i).toUpperCase()); 1735 return; 1736 } 1737 1738 switch (c) 1739 { 1740 case 'a': 1741 buffer.append('A'); 1742 break; 1743 case 'b': 1744 buffer.append('B'); 1745 break; 1746 case 'c': 1747 buffer.append('C'); 1748 break; 1749 case 'd': 1750 buffer.append('D'); 1751 break; 1752 case 'e': 1753 buffer.append('E'); 1754 break; 1755 case 'f': 1756 buffer.append('F'); 1757 break; 1758 case 'g': 1759 buffer.append('G'); 1760 break; 1761 case 'h': 1762 buffer.append('H'); 1763 break; 1764 case 'i': 1765 buffer.append('I'); 1766 break; 1767 case 'j': 1768 buffer.append('J'); 1769 break; 1770 case 'k': 1771 buffer.append('K'); 1772 break; 1773 case 'l': 1774 buffer.append('L'); 1775 break; 1776 case 'm': 1777 buffer.append('M'); 1778 break; 1779 case 'n': 1780 buffer.append('N'); 1781 break; 1782 case 'o': 1783 buffer.append('O'); 1784 break; 1785 case 'p': 1786 buffer.append('P'); 1787 break; 1788 case 'q': 1789 buffer.append('Q'); 1790 break; 1791 case 'r': 1792 buffer.append('R'); 1793 break; 1794 case 's': 1795 buffer.append('S'); 1796 break; 1797 case 't': 1798 buffer.append('T'); 1799 break; 1800 case 'u': 1801 buffer.append('U'); 1802 break; 1803 case 'v': 1804 buffer.append('V'); 1805 break; 1806 case 'w': 1807 buffer.append('W'); 1808 break; 1809 case 'x': 1810 buffer.append('X'); 1811 break; 1812 case 'y': 1813 buffer.append('Y'); 1814 break; 1815 case 'z': 1816 buffer.append('Z'); 1817 break; 1818 default: 1819 buffer.append(c); 1820 } 1821 } 1822 } 1823 1824 1825 1826 /** 1827 * Appends an uppercase string representation of the contents of the given 1828 * byte array to the provided buffer, optionally trimming leading and trailing 1829 * spaces. This implementation presumes that the provided string will contain 1830 * only ASCII characters and is optimized for that case. However, if a 1831 * non-ASCII character is encountered it will fall back on a more expensive 1832 * algorithm that will work properly for non-ASCII characters. 1833 * 1834 * @param b The byte array for which to obtain the uppercase string 1835 * representation. 1836 * @param buffer The buffer to which the uppercase form of the string should 1837 * be appended. 1838 * @param trim Indicates whether leading and trailing spaces should be 1839 * omitted from the string representation. 1840 */ 1841 public static void toUpperCase(byte[] b, StringBuilder buffer, boolean trim) 1842 { 1843 if (b == null) 1844 { 1845 return; 1846 } 1847 1848 int length = b.length; 1849 for (int i=0; i < length; i++) 1850 { 1851 if ((b[i] & 0x7F) != b[i]) 1852 { 1853 try 1854 { 1855 buffer.append(new String(b, i, (length-i), "UTF-8").toUpperCase()); 1856 } 1857 catch (Exception e) 1858 { 1859 logger.traceException(e); 1860 buffer.append(new String(b, i, (length - i)).toUpperCase()); 1861 } 1862 break; 1863 } 1864 1865 int bufferLength = buffer.length(); 1866 switch (b[i]) 1867 { 1868 case ' ': 1869 // If we don't care about trimming, then we can always append the 1870 // space. Otherwise, only do so if there are other characters in the value. 1871 if (trim && bufferLength == 0) 1872 { 1873 break; 1874 } 1875 1876 buffer.append(' '); 1877 break; 1878 case 'a': 1879 buffer.append('A'); 1880 break; 1881 case 'b': 1882 buffer.append('B'); 1883 break; 1884 case 'c': 1885 buffer.append('C'); 1886 break; 1887 case 'd': 1888 buffer.append('D'); 1889 break; 1890 case 'e': 1891 buffer.append('E'); 1892 break; 1893 case 'f': 1894 buffer.append('F'); 1895 break; 1896 case 'g': 1897 buffer.append('G'); 1898 break; 1899 case 'h': 1900 buffer.append('H'); 1901 break; 1902 case 'i': 1903 buffer.append('I'); 1904 break; 1905 case 'j': 1906 buffer.append('J'); 1907 break; 1908 case 'k': 1909 buffer.append('K'); 1910 break; 1911 case 'l': 1912 buffer.append('L'); 1913 break; 1914 case 'm': 1915 buffer.append('M'); 1916 break; 1917 case 'n': 1918 buffer.append('N'); 1919 break; 1920 case 'o': 1921 buffer.append('O'); 1922 break; 1923 case 'p': 1924 buffer.append('P'); 1925 break; 1926 case 'q': 1927 buffer.append('Q'); 1928 break; 1929 case 'r': 1930 buffer.append('R'); 1931 break; 1932 case 's': 1933 buffer.append('S'); 1934 break; 1935 case 't': 1936 buffer.append('T'); 1937 break; 1938 case 'u': 1939 buffer.append('U'); 1940 break; 1941 case 'v': 1942 buffer.append('V'); 1943 break; 1944 case 'w': 1945 buffer.append('W'); 1946 break; 1947 case 'x': 1948 buffer.append('X'); 1949 break; 1950 case 'y': 1951 buffer.append('Y'); 1952 break; 1953 case 'z': 1954 buffer.append('Z'); 1955 break; 1956 default: 1957 buffer.append((char) b[i]); 1958 } 1959 } 1960 1961 if (trim) 1962 { 1963 // Strip off any trailing spaces. 1964 for (int i=buffer.length()-1; i > 0; i--) 1965 { 1966 if (buffer.charAt(i) == ' ') 1967 { 1968 buffer.delete(i, i+1); 1969 } 1970 else 1971 { 1972 break; 1973 } 1974 } 1975 } 1976 } 1977 1978 1979 1980 /** 1981 * Append a string to a string builder, escaping any double quotes 1982 * according to the StringValue production in RFC 3641. 1983 * <p> 1984 * In RFC 3641 the StringValue production looks like this: 1985 * 1986 * <pre> 1987 * StringValue = dquote *SafeUTF8Character dquote 1988 * dquote = %x22 ; " (double quote) 1989 * SafeUTF8Character = %x00-21 / %x23-7F / ; ASCII minus dquote 1990 * dquote dquote / ; escaped double quote 1991 * %xC0-DF %x80-BF / ; 2 byte UTF-8 character 1992 * %xE0-EF 2(%x80-BF) / ; 3 byte UTF-8 character 1993 * %xF0-F7 3(%x80-BF) ; 4 byte UTF-8 character 1994 * </pre> 1995 * 1996 * <p> 1997 * That is, strings are surrounded by double-quotes and any internal 1998 * double-quotes are doubled up. 1999 * 2000 * @param builder 2001 * The string builder. 2002 * @param string 2003 * The string to escape and append. 2004 * @return Returns the string builder. 2005 */ 2006 public static StringBuilder toRFC3641StringValue(StringBuilder builder, 2007 String string) 2008 { 2009 // Initial double-quote. 2010 builder.append('"'); 2011 2012 for (char c : string.toCharArray()) 2013 { 2014 if (c == '"') 2015 { 2016 // Internal double-quotes are escaped using a double-quote. 2017 builder.append('"'); 2018 } 2019 builder.append(c); 2020 } 2021 2022 // Trailing double-quote. 2023 builder.append('"'); 2024 2025 return builder; 2026 } 2027 2028 2029 2030 /** 2031 * Retrieves a string array containing the contents of the provided 2032 * list of strings. 2033 * 2034 * @param stringList 2035 * The string list to convert to an array. 2036 * @return A string array containing the contents of the provided list 2037 * of strings. 2038 */ 2039 public static String[] listToArray(List<String> stringList) 2040 { 2041 if (stringList == null) 2042 { 2043 return null; 2044 } 2045 2046 String[] stringArray = new String[stringList.size()]; 2047 stringList.toArray(stringArray); 2048 return stringArray; 2049 } 2050 2051 /** 2052 * Retrieves an array list containing the contents of the provided array. 2053 * 2054 * @param stringArray The string array to convert to an array list. 2055 * 2056 * @return An array list containing the contents of the provided array. 2057 */ 2058 public static ArrayList<String> arrayToList(String... stringArray) 2059 { 2060 if (stringArray == null) 2061 { 2062 return null; 2063 } 2064 2065 ArrayList<String> stringList = new ArrayList<>(stringArray.length); 2066 Collections.addAll(stringList, stringArray); 2067 return stringList; 2068 } 2069 2070 2071 /** 2072 * Attempts to delete the specified file or directory. If it is a directory, 2073 * then any files or subdirectories that it contains will be recursively 2074 * deleted as well. 2075 * 2076 * @param file 2077 * The file or directory to be removed. 2078 * @return {@code true} if the specified file and any subordinates are all 2079 * successfully removed, or {@code false} if at least one element in 2080 * the subtree could not be removed or file does not exists. 2081 */ 2082 public static boolean recursiveDelete(File file) 2083 { 2084 if (file.exists()) 2085 { 2086 boolean successful = true; 2087 if (file.isDirectory()) 2088 { 2089 File[] childList = file.listFiles(); 2090 if (childList != null) 2091 { 2092 for (File f : childList) 2093 { 2094 successful &= recursiveDelete(f); 2095 } 2096 } 2097 } 2098 2099 return successful & file.delete(); 2100 } 2101 return false; 2102 } 2103 2104 2105 2106 /** 2107 * Moves the indicated file to the specified directory by creating a new file 2108 * in the target directory, copying the contents of the existing file, and 2109 * removing the existing file. The file to move must exist and must be a 2110 * file. The target directory must exist, must be a directory, and must not 2111 * be the directory in which the file currently resides. 2112 * 2113 * @param fileToMove The file to move to the target directory. 2114 * @param targetDirectory The directory into which the file should be moved. 2115 * 2116 * @throws IOException If a problem occurs while attempting to move the 2117 * file. 2118 */ 2119 public static void moveFile(File fileToMove, File targetDirectory) 2120 throws IOException 2121 { 2122 if (! fileToMove.exists()) 2123 { 2124 LocalizableMessage message = ERR_MOVEFILE_NO_SUCH_FILE.get(fileToMove.getPath()); 2125 throw new IOException(message.toString()); 2126 } 2127 2128 if (! fileToMove.isFile()) 2129 { 2130 LocalizableMessage message = ERR_MOVEFILE_NOT_FILE.get(fileToMove.getPath()); 2131 throw new IOException(message.toString()); 2132 } 2133 2134 if (! targetDirectory.exists()) 2135 { 2136 LocalizableMessage message = 2137 ERR_MOVEFILE_NO_SUCH_DIRECTORY.get(targetDirectory.getPath()); 2138 throw new IOException(message.toString()); 2139 } 2140 2141 if (! targetDirectory.isDirectory()) 2142 { 2143 LocalizableMessage message = 2144 ERR_MOVEFILE_NOT_DIRECTORY.get(targetDirectory.getPath()); 2145 throw new IOException(message.toString()); 2146 } 2147 2148 String newFilePath = targetDirectory.getPath() + File.separator + 2149 fileToMove.getName(); 2150 FileInputStream inputStream = new FileInputStream(fileToMove); 2151 FileOutputStream outputStream = new FileOutputStream(newFilePath, false); 2152 byte[] buffer = new byte[8192]; 2153 while (true) 2154 { 2155 int bytesRead = inputStream.read(buffer); 2156 if (bytesRead < 0) 2157 { 2158 break; 2159 } 2160 2161 outputStream.write(buffer, 0, bytesRead); 2162 } 2163 2164 outputStream.flush(); 2165 outputStream.close(); 2166 inputStream.close(); 2167 fileToMove.delete(); 2168 } 2169 2170 /** 2171 * Renames the source file to the target file. If the target file exists 2172 * it is first deleted. The rename and delete operation return values 2173 * are checked for success and if unsuccessful, this method throws an 2174 * exception. 2175 * 2176 * @param fileToRename The file to rename. 2177 * @param target The file to which {@code fileToRename} will be 2178 * moved. 2179 * @throws IOException If a problem occurs while attempting to rename the 2180 * file. On the Windows platform, this typically 2181 * indicates that the file is in use by this or another 2182 * application. 2183 */ 2184 public static void renameFile(File fileToRename, File target) 2185 throws IOException { 2186 if (fileToRename != null && target != null) 2187 { 2188 synchronized(target) 2189 { 2190 if (target.exists() && !target.delete()) 2191 { 2192 LocalizableMessage message = 2193 ERR_RENAMEFILE_CANNOT_DELETE_TARGET.get(target.getPath()); 2194 throw new IOException(message.toString()); 2195 } 2196 } 2197 if (!fileToRename.renameTo(target)) 2198 { 2199 LocalizableMessage message = ERR_RENAMEFILE_CANNOT_RENAME.get( 2200 fileToRename.getPath(), target.getPath()); 2201 throw new IOException(message.toString()); 2202 2203 } 2204 } 2205 } 2206 2207 2208 /** 2209 * Indicates whether the provided path refers to a relative path rather than 2210 * an absolute path. 2211 * 2212 * @param path The path string for which to make the determination. 2213 * 2214 * @return {@code true} if the provided path is relative, or 2215 * {@code false} if it is absolute. 2216 */ 2217 public static boolean isRelativePath(String path) 2218 { 2219 File f = new File(path); 2220 return !f.isAbsolute(); 2221 } 2222 2223 2224 2225 /** 2226 * Retrieves a {@code File} object corresponding to the specified path. 2227 * If the given path is an absolute path, then it will be used. If the path 2228 * is relative, then it will be interpreted as if it were relative to the 2229 * Directory Server root. 2230 * 2231 * @param path The path string to be retrieved as a {@code File} 2232 * 2233 * @return A {@code File} object that corresponds to the specified path. 2234 */ 2235 public static File getFileForPath(String path) 2236 { 2237 File f = new File (path); 2238 2239 if (f.isAbsolute()) 2240 { 2241 return f; 2242 } 2243 else 2244 { 2245 return new File(DirectoryServer.getInstanceRoot() + File.separator + 2246 path); 2247 } 2248 } 2249 2250 /** 2251 * Retrieves a {@code File} object corresponding to the specified path. 2252 * If the given path is an absolute path, then it will be used. If the path 2253 * is relative, then it will be interpreted as if it were relative to the 2254 * Directory Server root. 2255 * 2256 * @param path 2257 * The path string to be retrieved as a {@code File}. 2258 * @param serverContext 2259 * The server context. 2260 * 2261 * @return A {@code File} object that corresponds to the specified path. 2262 */ 2263 public static File getFileForPath(String path, ServerContext serverContext) 2264 { 2265 File f = new File (path); 2266 2267 if (f.isAbsolute()) 2268 { 2269 return f; 2270 } 2271 else 2272 { 2273 return new File(serverContext.getInstanceRoot() + File.separator + 2274 path); 2275 } 2276 } 2277 2278 2279 2280 /** 2281 * Creates a new, blank entry with the given DN. It will contain only the 2282 * attribute(s) contained in the RDN. The choice of objectclasses will be 2283 * based on the RDN attribute. If there is a single RDN attribute, then the 2284 * following mapping will be used: 2285 * <BR> 2286 * <UL> 2287 * <LI>c attribute :: country objectclass</LI> 2288 * <LI>dc attribute :: domain objectclass</LI> 2289 * <LI>o attribute :: organization objectclass</LI> 2290 * <LI>ou attribute :: organizationalUnit objectclass</LI> 2291 * </UL> 2292 * <BR> 2293 * Any other single RDN attribute types, or any case in which there are 2294 * multiple RDN attributes, will use the untypedObject objectclass. If the 2295 * RDN includes one or more attributes that are not allowed in the 2296 * untypedObject objectclass, then the extensibleObject class will also be 2297 * added. Note that this method cannot be used to generate an entry 2298 * with an empty or null DN. 2299 * 2300 * @param dn The DN to use for the entry. 2301 * 2302 * @return The entry created with the provided DN. 2303 */ 2304 public static Entry createEntry(DN dn) 2305 { 2306 // If the provided DN was null or empty, then return null because we don't 2307 // support it. 2308 if (dn == null || dn.isRootDN()) 2309 { 2310 return null; 2311 } 2312 2313 2314 // Get the information about the RDN attributes. 2315 RDN rdn = dn.rdn(); 2316 int numAVAs = rdn.getNumValues(); 2317 2318 // If there is only one RDN attribute, then see which objectclass we should use. 2319 ObjectClass structuralClass = DirectoryServer.getObjectClass(getObjectClassName(rdn, numAVAs)); 2320 2321 // Get the top and untypedObject classes to include in the entry. 2322 LinkedHashMap<ObjectClass,String> objectClasses = new LinkedHashMap<>(3); 2323 2324 objectClasses.put(DirectoryServer.getTopObjectClass(), OC_TOP); 2325 objectClasses.put(structuralClass, structuralClass.getNameOrOID()); 2326 2327 2328 // Iterate through the RDN attributes and add them to the set of user or 2329 // operational attributes. 2330 LinkedHashMap<AttributeType,List<Attribute>> userAttributes = new LinkedHashMap<>(); 2331 LinkedHashMap<AttributeType,List<Attribute>> operationalAttributes = new LinkedHashMap<>(); 2332 2333 boolean extensibleObjectAdded = false; 2334 for (int i=0; i < numAVAs; i++) 2335 { 2336 AttributeType attrType = rdn.getAttributeType(i); 2337 ByteString attrValue = rdn.getAttributeValue(i); 2338 String attrName = rdn.getAttributeName(i); 2339 2340 // First, see if this type is allowed by the untypedObject class. If not, 2341 // then we'll need to include the extensibleObject class. 2342 if (!structuralClass.isRequiredOrOptional(attrType) && !extensibleObjectAdded) 2343 { 2344 ObjectClass extensibleObjectOC = 2345 DirectoryServer.getObjectClass(OC_EXTENSIBLE_OBJECT_LC); 2346 if (extensibleObjectOC == null) 2347 { 2348 extensibleObjectOC = 2349 DirectoryServer.getDefaultObjectClass(OC_EXTENSIBLE_OBJECT); 2350 } 2351 objectClasses.put(extensibleObjectOC, OC_EXTENSIBLE_OBJECT); 2352 extensibleObjectAdded = true; 2353 } 2354 2355 2356 // Create the attribute and add it to the appropriate map. 2357 if (attrType.isOperational()) 2358 { 2359 addAttributeValue(operationalAttributes, attrType, attrName, attrValue); 2360 } 2361 else 2362 { 2363 addAttributeValue(userAttributes, attrType, attrName, attrValue); 2364 } 2365 } 2366 2367 2368 // Create and return the entry. 2369 return new Entry(dn, objectClasses, userAttributes, operationalAttributes); 2370 } 2371 2372 private static String getObjectClassName(RDN rdn, int numAVAs) 2373 { 2374 if (numAVAs == 1) 2375 { 2376 final AttributeType attrType = rdn.getAttributeType(0); 2377 if (attrType.hasName(ATTR_C)) 2378 { 2379 return OC_COUNTRY; 2380 } 2381 else if (attrType.hasName(ATTR_DC)) 2382 { 2383 return OC_DOMAIN; 2384 } 2385 else if (attrType.hasName(ATTR_O)) 2386 { 2387 return OC_ORGANIZATION; 2388 } 2389 else if (attrType.hasName(ATTR_OU)) 2390 { 2391 return OC_ORGANIZATIONAL_UNIT_LC; 2392 } 2393 } 2394 return OC_UNTYPED_OBJECT_LC; 2395 } 2396 2397 private static void addAttributeValue(LinkedHashMap<AttributeType, List<Attribute>> attrs, 2398 AttributeType attrType, String attrName, ByteString attrValue) 2399 { 2400 List<Attribute> attrList = attrs.get(attrType); 2401 if (attrList != null && !attrList.isEmpty()) 2402 { 2403 AttributeBuilder builder = new AttributeBuilder(attrList.get(0)); 2404 builder.add(attrValue); 2405 attrList.set(0, builder.toAttribute()); 2406 } 2407 else 2408 { 2409 AttributeBuilder builder = new AttributeBuilder(attrType, attrName); 2410 builder.add(attrValue); 2411 attrs.put(attrType, builder.toAttributeList()); 2412 } 2413 } 2414 2415 /** 2416 * Retrieves a user-friendly string that indicates the length of time (in 2417 * days, hours, minutes, and seconds) in the specified number of seconds. 2418 * 2419 * @param numSeconds The number of seconds to be converted to a more 2420 * user-friendly value. 2421 * 2422 * @return The user-friendly representation of the specified number of 2423 * seconds. 2424 */ 2425 public static LocalizableMessage secondsToTimeString(long numSeconds) 2426 { 2427 if (numSeconds < 60) 2428 { 2429 // We can express it in seconds. 2430 return INFO_TIME_IN_SECONDS.get(numSeconds); 2431 } 2432 else if (numSeconds < 3600) 2433 { 2434 // We can express it in minutes and seconds. 2435 long m = numSeconds / 60; 2436 long s = numSeconds % 60; 2437 return INFO_TIME_IN_MINUTES_SECONDS.get(m, s); 2438 } 2439 else if (numSeconds < 86400) 2440 { 2441 // We can express it in hours, minutes, and seconds. 2442 long h = numSeconds / 3600; 2443 long m = (numSeconds % 3600) / 60; 2444 long s = numSeconds % 3600 % 60; 2445 return INFO_TIME_IN_HOURS_MINUTES_SECONDS.get(h, m, s); 2446 } 2447 else 2448 { 2449 // We can express it in days, hours, minutes, and seconds. 2450 long d = numSeconds / 86400; 2451 long h = (numSeconds % 86400) / 3600; 2452 long m = (numSeconds % 86400 % 3600) / 60; 2453 long s = numSeconds % 86400 % 3600 % 60; 2454 return INFO_TIME_IN_DAYS_HOURS_MINUTES_SECONDS.get(d, h, m, s); 2455 } 2456 } 2457 2458 /** 2459 * Checks that no more that one of a set of arguments is present. This 2460 * utility should be used after argument parser has parsed a set of 2461 * arguments. 2462 * 2463 * @param args to test for the presence of more than one 2464 * @throws ArgumentException if more than one of {@code args} is 2465 * present and containing an error message identifying the 2466 * arguments in violation 2467 */ 2468 public static void checkOnlyOneArgPresent(Argument... args) 2469 throws ArgumentException 2470 { 2471 if (args != null) { 2472 for (Argument arg : args) { 2473 for (Argument otherArg : args) { 2474 if (arg != otherArg && arg.isPresent() && otherArg.isPresent()) { 2475 throw new ArgumentException( 2476 ToolMessages.ERR_INCOMPATIBLE_ARGUMENTS.get( 2477 arg.getName(), otherArg.getName())); 2478 } 2479 } 2480 } 2481 } 2482 } 2483 2484 /** 2485 * Converts a string representing a time in "yyyyMMddHHmmss.SSS'Z'" or 2486 * "yyyyMMddHHmmss" to a {@code Date}. 2487 * 2488 * @param timeStr string formatted appropriately 2489 * @return Date object; null if {@code timeStr} is null 2490 * @throws ParseException if there was a problem converting the string to 2491 * a {@code Date}. 2492 */ 2493 public static Date parseDateTimeString(String timeStr) throws ParseException 2494 { 2495 Date dateTime = null; 2496 if (timeStr != null) 2497 { 2498 if (timeStr.endsWith("Z")) 2499 { 2500 try 2501 { 2502 SimpleDateFormat dateFormat = 2503 new SimpleDateFormat(DATE_FORMAT_GENERALIZED_TIME); 2504 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 2505 dateFormat.setLenient(true); 2506 dateTime = dateFormat.parse(timeStr); 2507 } 2508 catch (ParseException pe) 2509 { 2510 // Best effort: try with GMT time. 2511 SimpleDateFormat dateFormat = 2512 new SimpleDateFormat(DATE_FORMAT_GMT_TIME); 2513 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 2514 dateFormat.setLenient(true); 2515 dateTime = dateFormat.parse(timeStr); 2516 } 2517 } 2518 else 2519 { 2520 SimpleDateFormat dateFormat = 2521 new SimpleDateFormat(DATE_FORMAT_COMPACT_LOCAL_TIME); 2522 dateFormat.setLenient(true); 2523 dateTime = dateFormat.parse(timeStr); 2524 } 2525 } 2526 return dateTime; 2527 } 2528 2529 /** 2530 * Formats a Date to String representation in "yyyyMMddHHmmss'Z'". 2531 * 2532 * @param date to format; null if {@code date} is null 2533 * @return string representation of the date 2534 */ 2535 public static String formatDateTimeString(Date date) 2536 { 2537 String timeStr = null; 2538 if (date != null) 2539 { 2540 SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME); 2541 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 2542 timeStr = dateFormat.format(date); 2543 } 2544 return timeStr; 2545 } 2546 2547 /** 2548 * Indicates whether or not a string represents a syntactically correct 2549 * email address. 2550 * 2551 * @param addr to validate 2552 * @return boolean where {@code true} indicates that the string is a 2553 * syntactically correct email address 2554 */ 2555 public static boolean isEmailAddress(String addr) { 2556 2557 // This just does basic syntax checking. Perhaps we 2558 // might want to be stricter about this. 2559 return addr != null && addr.contains("@") && addr.contains("."); 2560 2561 } 2562 2563 2564 2565 /** 2566 * Writes the contents of the provided buffer to the client, 2567 * terminating the connection if the write is unsuccessful for too 2568 * long (e.g., if the client is unresponsive or there is a network 2569 * problem). If possible, it will attempt to use the selector returned 2570 * by the {@code ClientConnection.getWriteSelector} method, but it is 2571 * capable of working even if that method returns {@code null}. <BR> 2572 * 2573 * Note that the original position and limit values will not be 2574 * preserved, so if that is important to the caller, then it should 2575 * record them before calling this method and restore them after it 2576 * returns. 2577 * 2578 * @param clientConnection 2579 * The client connection to which the data is to be written. 2580 * @param buffer 2581 * The data to be written to the client. 2582 * @return {@code true} if all the data in the provided buffer was 2583 * written to the client and the connection may remain 2584 * established, or {@code false} if a problem occurred 2585 * and the client connection is no longer valid. Note that if 2586 * this method does return {@code false}, then it must 2587 * have already disconnected the client. 2588 * @throws IOException 2589 * If a problem occurs while attempting to write data to the 2590 * client. The caller will be responsible for catching this 2591 * and terminating the client connection. 2592 */ 2593 public static boolean writeWithTimeout(ClientConnection clientConnection, 2594 ByteBuffer buffer) throws IOException 2595 { 2596 SocketChannel socketChannel = clientConnection.getSocketChannel(); 2597 long startTime = System.currentTimeMillis(); 2598 long waitTime = clientConnection.getMaxBlockedWriteTimeLimit(); 2599 if (waitTime <= 0) 2600 { 2601 // We won't support an infinite time limit, so fall back to using 2602 // five minutes, which is a very long timeout given that we're 2603 // blocking a worker thread. 2604 waitTime = 300000L; 2605 } 2606 2607 long stopTime = startTime + waitTime; 2608 2609 Selector selector = clientConnection.getWriteSelector(); 2610 if (selector == null) 2611 { 2612 // The client connection does not provide a selector, so we'll 2613 // fall back 2614 // to a more inefficient way that will work without a selector. 2615 while (buffer.hasRemaining() 2616 && System.currentTimeMillis() < stopTime) 2617 { 2618 if (socketChannel.write(buffer) < 0) 2619 { 2620 // The client connection has been closed. 2621 return false; 2622 } 2623 } 2624 2625 if (buffer.hasRemaining()) 2626 { 2627 // If we've gotten here, then the write timed out. 2628 return false; 2629 } 2630 2631 return true; 2632 } 2633 2634 // Register with the selector for handling write operations. 2635 SelectionKey key = 2636 socketChannel.register(selector, SelectionKey.OP_WRITE); 2637 2638 try 2639 { 2640 selector.select(waitTime); 2641 while (buffer.hasRemaining()) 2642 { 2643 long currentTime = System.currentTimeMillis(); 2644 if (currentTime >= stopTime) 2645 { 2646 // We've been blocked for too long. 2647 return false; 2648 } 2649 else 2650 { 2651 waitTime = stopTime - currentTime; 2652 } 2653 2654 Iterator<SelectionKey> iterator = 2655 selector.selectedKeys().iterator(); 2656 while (iterator.hasNext()) 2657 { 2658 SelectionKey k = iterator.next(); 2659 if (k.isWritable()) 2660 { 2661 int bytesWritten = socketChannel.write(buffer); 2662 if (bytesWritten < 0) 2663 { 2664 // The client connection has been closed. 2665 return false; 2666 } 2667 2668 iterator.remove(); 2669 } 2670 } 2671 2672 if (buffer.hasRemaining()) 2673 { 2674 selector.select(waitTime); 2675 } 2676 } 2677 2678 return true; 2679 } 2680 finally 2681 { 2682 if (key.isValid()) 2683 { 2684 key.cancel(); 2685 selector.selectNow(); 2686 } 2687 } 2688 } 2689 2690 2691 2692 /** 2693 * Add all of the superior objectclasses to the specified objectclass 2694 * map if they don't already exist. Used by add and import-ldif to 2695 * add missing superior objectclasses to entries that don't have them. 2696 * 2697 * @param objectClasses A Map of objectclasses. 2698 */ 2699 public static void addSuperiorObjectClasses(Map<ObjectClass, 2700 String> objectClasses) { 2701 HashSet<ObjectClass> additionalClasses = null; 2702 for (ObjectClass oc : objectClasses.keySet()) 2703 { 2704 for(ObjectClass superiorClass : oc.getSuperiorClasses()) 2705 { 2706 if (! objectClasses.containsKey(superiorClass)) 2707 { 2708 if (additionalClasses == null) 2709 { 2710 additionalClasses = new HashSet<>(); 2711 } 2712 2713 additionalClasses.add(superiorClass); 2714 } 2715 } 2716 } 2717 2718 if (additionalClasses != null) 2719 { 2720 for (ObjectClass oc : additionalClasses) 2721 { 2722 addObjectClassChain(oc, objectClasses); 2723 } 2724 } 2725 } 2726 2727 private static void addObjectClassChain(ObjectClass objectClass, 2728 Map<ObjectClass, String> objectClasses) 2729 { 2730 if (objectClasses != null){ 2731 if (! objectClasses.containsKey(objectClass)) 2732 { 2733 objectClasses.put(objectClass, objectClass.getNameOrOID()); 2734 } 2735 2736 for(ObjectClass superiorClass : objectClass.getSuperiorClasses()) 2737 { 2738 if (! objectClasses.containsKey(superiorClass)) 2739 { 2740 addObjectClassChain(superiorClass, objectClasses); 2741 } 2742 } 2743 } 2744 } 2745 2746 2747 /** 2748 * Closes the provided {@link Closeable}'s ignoring any errors which 2749 * occurred. 2750 * 2751 * @param closeables The closeables to be closed, which may be 2752 * {@code null}. 2753 */ 2754 public static void close(Closeable... closeables) 2755 { 2756 if (closeables == null) 2757 { 2758 return; 2759 } 2760 close(Arrays.asList(closeables)); 2761 } 2762 2763 /** 2764 * Closes the provided {@link Closeable}'s ignoring any errors which occurred. 2765 * 2766 * @param closeables 2767 * The closeables to be closed, which may be {@code null}. 2768 */ 2769 public static void close(Collection<? extends Closeable> closeables) 2770 { 2771 if (closeables == null) 2772 { 2773 return; 2774 } 2775 for (Closeable closeable : closeables) 2776 { 2777 if (closeable != null) 2778 { 2779 try 2780 { 2781 closeable.close(); 2782 } 2783 catch (IOException ignored) 2784 { 2785 logger.traceException(ignored); 2786 } 2787 } 2788 } 2789 } 2790 2791 /** 2792 * Closes the provided {@link InitialContext}'s ignoring any errors which occurred. 2793 * 2794 * @param ctxs 2795 * The contexts to be closed, which may be {@code null}. 2796 */ 2797 public static void close(InitialContext... ctxs) 2798 { 2799 if (ctxs == null) 2800 { 2801 return; 2802 } 2803 for (InitialContext ctx : ctxs) 2804 { 2805 if (ctx != null) 2806 { 2807 try 2808 { 2809 ctx.close(); 2810 } 2811 catch (NamingException ignored) 2812 { 2813 // ignore 2814 } 2815 } 2816 } 2817 } 2818 2819 /** 2820 * Calls {@link Thread#sleep(long)}, surrounding it with the mandatory 2821 * {@code try} / {@code catch(InterruptedException)} block. 2822 * 2823 * @param millis 2824 * the length of time to sleep in milliseconds 2825 */ 2826 public static void sleep(long millis) 2827 { 2828 try 2829 { 2830 Thread.sleep(millis); 2831 } 2832 catch (InterruptedException wokenUp) 2833 { 2834 // ignore 2835 } 2836 } 2837 2838 /** 2839 * Test if the provided message corresponds to the provided descriptor. 2840 * 2841 * @param msg 2842 * The i18n message. 2843 * @param desc 2844 * The message descriptor. 2845 * @return {@code true} if message corresponds to descriptor 2846 */ 2847 public static boolean hasDescriptor(LocalizableMessage msg, 2848 LocalizableMessageDescriptor.Arg0 desc) 2849 { 2850 return msg.ordinal() == desc.ordinal() 2851 && msg.resourceName().equals(desc.resourceName()); 2852 } 2853 2854 /** 2855 * Test if the provided message corresponds to the provided descriptor. 2856 * 2857 * @param msg 2858 * The i18n message. 2859 * @param desc 2860 * The message descriptor. 2861 * @return {@code true} if message corresponds to descriptor 2862 */ 2863 public static boolean hasDescriptor(LocalizableMessage msg, 2864 LocalizableMessageDescriptor.Arg1 desc) 2865 { 2866 return msg.ordinal() == desc.ordinal() 2867 && msg.resourceName().equals(desc.resourceName()); 2868 } 2869 2870 /** 2871 * Test if the provided message corresponds to the provided descriptor. 2872 * 2873 * @param msg 2874 * The i18n message. 2875 * @param desc 2876 * The message descriptor. 2877 * @return {@code true} if message corresponds to descriptor 2878 */ 2879 public static boolean hasDescriptor(LocalizableMessage msg, 2880 LocalizableMessageDescriptor.Arg2 desc) 2881 { 2882 return msg.ordinal() == desc.ordinal() 2883 && msg.resourceName().equals(desc.resourceName()); 2884 } 2885 2886 /** 2887 * Test if the provided message corresponds to the provided descriptor. 2888 * 2889 * @param msg 2890 * The i18n message. 2891 * @param desc 2892 * The message descriptor. 2893 * @return {@code true} if message corresponds to descriptor 2894 */ 2895 public static boolean hasDescriptor(LocalizableMessage msg, 2896 LocalizableMessageDescriptor.Arg3 desc) 2897 { 2898 return msg.ordinal() == desc.ordinal() 2899 && msg.resourceName().equals(desc.resourceName()); 2900 } 2901 2902 /** 2903 * Test if the provided message corresponds to the provided descriptor. 2904 * 2905 * @param msg 2906 * The i18n message. 2907 * @param desc 2908 * The message descriptor. 2909 * @return {@code true} if message corresponds to descriptor 2910 */ 2911 public static boolean hasDescriptor(LocalizableMessage msg, 2912 LocalizableMessageDescriptor.Arg7 desc) 2913 { 2914 return msg.ordinal() == desc.ordinal() 2915 && msg.resourceName().equals(desc.resourceName()); 2916 } 2917 2918 /** 2919 * Returns an {@link Iterable} returning the passed in {@link Iterator}. THis 2920 * allows using methods returning Iterators with foreach statements. 2921 * <p> 2922 * For example, consider a method with this signature: 2923 * <p> 2924 * <code>public Iterator<String> myIteratorMethod();</code> 2925 * <p> 2926 * Classical use with for or while loop: 2927 * 2928 * <pre> 2929 * for (Iterator<String> it = myIteratorMethod(); it.hasNext();) 2930 * { 2931 * String s = it.next(); 2932 * // use it 2933 * } 2934 * 2935 * Iterator<String> it = myIteratorMethod(); 2936 * while(it.hasNext();) 2937 * { 2938 * String s = it.next(); 2939 * // use it 2940 * } 2941 * </pre> 2942 * 2943 * Improved use with foreach: 2944 * 2945 * <pre> 2946 * for (String s : StaticUtils.toIterable(myIteratorMethod())) 2947 * { 2948 * } 2949 * </pre> 2950 * 2951 * </p> 2952 * 2953 * @param <T> 2954 * the generic type of the passed in Iterator and for the returned 2955 * Iterable. 2956 * @param iterator 2957 * the Iterator that will be returned by the Iterable. 2958 * @return an Iterable returning the passed in Iterator 2959 */ 2960 public static <T> Iterable<T> toIterable(final Iterator<T> iterator) 2961 { 2962 return new Iterable<T>() 2963 { 2964 @Override 2965 public Iterator<T> iterator() 2966 { 2967 return iterator; 2968 } 2969 }; 2970 } 2971 2972 /** 2973 * Returns true if the version of the server is an OEM one, and therefore doesn't support the JE backend. 2974 * @return {@code true} if the version of the server is an OEM version and {@code false} otherwise. 2975 */ 2976 public static boolean isOEMVersion() 2977 { 2978 return !isClassAvailable("org.opends.server.backends.jeb.JEBackend"); 2979 } 2980 2981 /** 2982 * Returns true if the class is available in the classpath. 2983 * @param className the string representing the class to check. 2984 * @return {@code true} if the class is available in the classpath and {@code false} otherwise. 2985 */ 2986 public static boolean isClassAvailable(final String className) 2987 { 2988 try 2989 { 2990 Class.forName(className); 2991 return true; 2992 } 2993 catch (Exception e) 2994 { 2995 return false; 2996 } 2997 } 2998} 2999