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