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 }