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