001    /*
002     * Copyright 2011-2012 UnboundID Corp.
003     *
004     * This program is free software; you can redistribute it and/or modify
005     * it under the terms of the GNU General Public License (GPLv2 only)
006     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
007     * as published by the Free Software Foundation.
008     *
009     * This program is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRANTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012     * GNU General Public License for more details.
013     *
014     * You should have received a copy of the GNU General Public License
015     * along with this program; if not, see <http://www.gnu.org/licenses>.
016     */
017    
018    package com.unboundid.scim.sdk;
019    
020    
021    
022    import java.io.BufferedReader;
023    import java.io.File;
024    import java.io.IOException;
025    import java.io.InputStream;
026    import java.io.InputStreamReader;
027    import java.util.ArrayList;
028    import java.util.List;
029    import java.util.Map;
030    
031    
032    
033    /**
034     * This class provides a number of static utility functions.
035     */
036    public final class StaticUtils
037    {
038      /**
039       * Prevent this class from being instantiated.
040       */
041      private StaticUtils()
042      {
043        // No implementation is required.
044      }
045    
046    
047    
048      /**
049       * Retrieves an all-lowercase version of the provided string.
050       *
051       * @param  s  The string for which to retrieve the lowercase version.
052       *
053       * @return  An all-lowercase version of the provided string.
054       */
055      public static String toLowerCase(final String s)
056      {
057        if (s == null)
058        {
059          return null;
060        }
061    
062        final int length = s.length();
063        final char[] charArray = s.toCharArray();
064        for (int i=0; i < length; i++)
065        {
066          switch (charArray[i])
067          {
068            case 'A':
069              charArray[i] = 'a';
070              break;
071            case 'B':
072              charArray[i] = 'b';
073              break;
074            case 'C':
075              charArray[i] = 'c';
076              break;
077            case 'D':
078              charArray[i] = 'd';
079              break;
080            case 'E':
081              charArray[i] = 'e';
082              break;
083            case 'F':
084              charArray[i] = 'f';
085              break;
086            case 'G':
087              charArray[i] = 'g';
088              break;
089            case 'H':
090              charArray[i] = 'h';
091              break;
092            case 'I':
093              charArray[i] = 'i';
094              break;
095            case 'J':
096              charArray[i] = 'j';
097              break;
098            case 'K':
099              charArray[i] = 'k';
100              break;
101            case 'L':
102              charArray[i] = 'l';
103              break;
104            case 'M':
105              charArray[i] = 'm';
106              break;
107            case 'N':
108              charArray[i] = 'n';
109              break;
110            case 'O':
111              charArray[i] = 'o';
112              break;
113            case 'P':
114              charArray[i] = 'p';
115              break;
116            case 'Q':
117              charArray[i] = 'q';
118              break;
119            case 'R':
120              charArray[i] = 'r';
121              break;
122            case 'S':
123              charArray[i] = 's';
124              break;
125            case 'T':
126              charArray[i] = 't';
127              break;
128            case 'U':
129              charArray[i] = 'u';
130              break;
131            case 'V':
132              charArray[i] = 'v';
133              break;
134            case 'W':
135              charArray[i] = 'w';
136              break;
137            case 'X':
138              charArray[i] = 'x';
139              break;
140            case 'Y':
141              charArray[i] = 'y';
142              break;
143            case 'Z':
144              charArray[i] = 'z';
145              break;
146            default:
147              if (charArray[i] > 0x7F)
148              {
149                return s.toLowerCase();
150              }
151              break;
152          }
153        }
154    
155        return new String(charArray);
156      }
157    
158    
159    
160      /**
161       * Retrieves a single-line string representation of the stack trace for the
162       * provided {@code Throwable}.  It will include the unqualified name of the
163       * {@code Throwable} class, a list of source files and line numbers (if
164       * available) for the stack trace, and will also include the stack trace for
165       * the cause (if present).
166       *
167       * @param  t  The {@code Throwable} for which to retrieve the stack trace.
168       *
169       * @return  A single-line string representation of the stack trace for the
170       *          provided {@code Throwable}.
171       */
172      public static String getStackTrace(final Throwable t)
173      {
174        final StringBuilder buffer = new StringBuilder();
175        getStackTrace(t, buffer);
176        return buffer.toString();
177      }
178    
179    
180    
181      /**
182       * Appends a single-line string representation of the stack trace for the
183       * provided {@code Throwable} to the given buffer.  It will include the
184       * unqualified name of the {@code Throwable} class, a list of source files and
185       * line numbers (if available) for the stack trace, and will also include the
186       * stack trace for the cause (if present).
187       *
188       * @param  t       The {@code Throwable} for which to retrieve the stack
189       *                 trace.
190       * @param  buffer  The buffer to which the information should be appended.
191       */
192      public static void getStackTrace(final Throwable t,
193                                       final StringBuilder buffer)
194      {
195        buffer.append(t.getClass().getSimpleName());
196        buffer.append('(');
197    
198        final String message = t.getMessage();
199        if (message != null)
200        {
201          buffer.append("message='");
202          buffer.append(message);
203          buffer.append("', ");
204        }
205    
206        buffer.append("trace='");
207        getStackTrace(t.getStackTrace(), buffer);
208        buffer.append('\'');
209    
210        final Throwable cause = t.getCause();
211        if (cause != null)
212        {
213          buffer.append(", cause=");
214          getStackTrace(cause, buffer);
215        }
216        buffer.append(", revision=");
217        buffer.append(Version.REVISION_NUMBER);
218        buffer.append(')');
219      }
220    
221    
222    
223      /**
224       * Returns a single-line string representation of the stack trace.  It will
225       * include a list of source files and line numbers (if available) for the
226       * stack trace.
227       *
228       * @param  elements  The stack trace.
229       *
230       * @return  A single-line string representation of the stack trace.
231       */
232      public static String getStackTrace(final StackTraceElement[] elements)
233      {
234        final StringBuilder buffer = new StringBuilder();
235        getStackTrace(elements, buffer);
236        return buffer.toString();
237      }
238    
239    
240    
241      /**
242       * Appends a single-line string representation of the stack trace to the given
243       * buffer.  It will include a list of source files and line numbers
244       * (if available) for the stack trace.
245       *
246       * @param  elements  The stack trace.
247       * @param  buffer  The buffer to which the information should be appended.
248       */
249      public static void getStackTrace(final StackTraceElement[] elements,
250                                       final StringBuilder buffer)
251      {
252        for (int i=0; i < elements.length; i++)
253        {
254          if (i > 0)
255          {
256            buffer.append(" / ");
257          }
258    
259          buffer.append(elements[i].getMethodName());
260          buffer.append('(');
261          buffer.append(elements[i].getFileName());
262    
263          final int lineNumber = elements[i].getLineNumber();
264          if (lineNumber > 0)
265          {
266            buffer.append(':');
267            buffer.append(lineNumber);
268          }
269          buffer.append(')');
270        }
271      }
272    
273    
274    
275      /**
276       * Executes the specified command on the system and captures its output.  This
277       * will not return until the specified process has completed.
278       *
279       * @param  command           The command to execute.
280       * @param  args              The set of arguments to provide to the command.
281       * @param  workingDirectory  The working directory to use for the command, or
282       *                           <CODE>null</CODE> if the default directory
283       *                           should be used.
284       * @param  environment       The set of environment variables that should be
285       *                           set when executing the command, or
286       *                           <CODE>null</CODE> if none are needed.
287       * @param  output            The output generated by the command while it was
288       *                           running.  This will include both standard
289       *                           output and standard error.  It may be
290       *                           <CODE>null</CODE> if the output does not need to
291       *                           be captured.
292       *
293       * @return  The exit code for the command.
294       *
295       * @throws  IOException  If an I/O problem occurs while trying to execute the
296       *                       command.
297       *
298       * @throws  SecurityException  If the security policy will not allow the
299       *                             command to be executed.
300       *
301       * @throws InterruptedException If the current thread is interrupted by
302       *                              another thread while it is waiting, then
303       *                              the wait is ended and an InterruptedException
304       *                              is thrown.
305       */
306      public static int exec(final String command, final String[] args,
307                             final File workingDirectory,
308                             final Map<String,String> environment,
309                             final List<String> output)
310             throws IOException, SecurityException, InterruptedException
311      {
312        ArrayList<String> commandAndArgs = new ArrayList<String>();
313        commandAndArgs.add(command);
314        if ((args != null) && (args.length > 0))
315        {
316          for (String arg : args)
317          {
318            commandAndArgs.add(arg);
319          }
320        }
321    
322        ProcessBuilder processBuilder = new ProcessBuilder(commandAndArgs);
323        processBuilder.redirectErrorStream(true);
324    
325        if ((workingDirectory != null) && workingDirectory.isDirectory())
326        {
327          processBuilder.directory(workingDirectory);
328        }
329    
330        if ((environment != null) && (! environment.isEmpty()))
331        {
332          processBuilder.environment().putAll(environment);
333        }
334    
335        Process process = processBuilder.start();
336    
337        // We must exhaust stdout and stderr before calling waitfor. Since we
338        // redirected the error stream, we just have to read from stdout.
339        InputStream processStream =  process.getInputStream();
340        BufferedReader reader =
341            new BufferedReader(new InputStreamReader(processStream));
342        String line = null;
343    
344        try
345        {
346          while((line = reader.readLine()) != null)
347          {
348            if(output != null)
349            {
350              output.add(line);
351            }
352          }
353        }
354        catch(IOException ioe)
355        {
356          // If this happens, then we have no choice but to forcefully terminate
357          // the process.
358          try
359          {
360            process.destroy();
361          }
362          catch (Exception e)
363          {
364            Debug.debugException(e);
365          }
366    
367          throw ioe;
368        }
369        finally
370        {
371          try
372          {
373            reader.close();
374          }
375          catch(IOException e)
376          {
377            Debug.debugException(e);
378          }
379        }
380    
381        return process.waitFor();
382      }
383    }