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