You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

LangUtil.java 45KB

21 years ago
21 years ago
21 years ago
16 years ago
21 years ago
21 years ago
11 years ago
21 years ago
11 years ago
13 years ago
13 years ago
13 years ago
13 years ago
15 years ago
15 years ago
13 years ago
11 years ago
21 years ago
21 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
21 years ago
13 years ago
13 years ago
13 years ago
21 years ago
13 years ago
15 years ago
21 years ago
21 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
21 years ago
13 years ago
21 years ago
21 years ago
13 years ago
15 years ago
13 years ago
15 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500
  1. /* *******************************************************************
  2. * Copyright (c) 1999-2001 Xerox Corporation,
  3. * 2002 Palo Alto Research Center, Incorporated (PARC).
  4. * All rights reserved.
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Public License v1.0
  7. * which accompanies this distribution and is available at
  8. * http://www.eclipse.org/legal/epl-v10.html
  9. *
  10. * Contributors:
  11. * Xerox/PARC initial implementation
  12. * ******************************************************************/
  13. package org.aspectj.util;
  14. import java.io.ByteArrayOutputStream;
  15. import java.io.File;
  16. import java.io.IOException;
  17. import java.io.PrintWriter;
  18. import java.io.StringWriter;
  19. import java.lang.reflect.Array;
  20. import java.lang.reflect.InvocationTargetException;
  21. import java.security.PrivilegedActionException;
  22. import java.sql.SQLException;
  23. import java.util.ArrayList;
  24. import java.util.Arrays;
  25. import java.util.BitSet;
  26. import java.util.Collection;
  27. import java.util.Collections;
  28. import java.util.LinkedList;
  29. import java.util.List;
  30. import java.util.Map;
  31. import java.util.StringTokenizer;
  32. /**
  33. *
  34. */
  35. public class LangUtil {
  36. public static final String EOL;
  37. public static final String JRT_FS = "jrt-fs.jar";
  38. private static double vmVersion;
  39. /**
  40. * @return the vm version (1.1, 1.2, 1.3, 1.4, etc)
  41. */
  42. public static String getVmVersionString() {
  43. return Double.toString(vmVersion);
  44. }
  45. public static double getVmVersion() {
  46. return vmVersion;
  47. }
  48. static {
  49. StringWriter buf = new StringWriter();
  50. PrintWriter writer = new PrintWriter(buf);
  51. writer.println("");
  52. String eol = "\n";
  53. try {
  54. buf.close();
  55. StringBuffer sb = buf.getBuffer();
  56. if (sb != null) {
  57. eol = buf.toString();
  58. }
  59. } catch (Throwable t) {
  60. }
  61. EOL = eol;
  62. }
  63. static {
  64. // http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html
  65. // http://openjdk.java.net/jeps/223 "New Version-String Scheme"
  66. try {
  67. String vm = System.getProperty("java.version"); // JLS 20.18.7
  68. if (vm == null) {
  69. vm = System.getProperty("java.runtime.version");
  70. }
  71. if (vm == null) {
  72. vm = System.getProperty("java.vm.version");
  73. }
  74. if (vm == null) {
  75. new RuntimeException(
  76. "System properties appear damaged, cannot find: java.version/java.runtime.version/java.vm.version")
  77. .printStackTrace(System.err);
  78. vmVersion = 1.5;
  79. } else {
  80. // Version: [1-9][0-9]*((\.0)*\.[1-9][0-9]*)*
  81. // Care about the first set of digits and second set if first digit is 1
  82. try {
  83. List<Integer> numbers = getFirstNumbers(vm);
  84. if (numbers.get(0) == 1) {
  85. // Old school for 1.0 > 1.8
  86. vmVersion = numbers.get(0)+(numbers.get(1)/10d);
  87. } else {
  88. // numbers.get(0) is the major version (9 and above)
  89. // Note here the number will be 9 (or 10), *not* 1.9 or 1.10
  90. vmVersion = numbers.get(0);
  91. }
  92. } catch (Throwable t) {
  93. // Give up
  94. vmVersion = 1.5;
  95. }
  96. }
  97. } catch (Throwable t) {
  98. new RuntimeException(
  99. "System properties appear damaged, cannot find: java.version/java.runtime.version/java.vm.version", t)
  100. .printStackTrace(System.err);
  101. vmVersion = 1.5;
  102. }
  103. }
  104. private static List<Integer> getFirstNumbers(String vm) {
  105. List<Integer> result = new ArrayList<Integer>();
  106. StringTokenizer st = new StringTokenizer(vm,".-_");
  107. try {
  108. result.add(Integer.parseInt(st.nextToken()));
  109. result.add(Integer.parseInt(st.nextToken()));
  110. } catch (Exception e) {
  111. // NoSuchElementException if no more tokens
  112. // NumberFormatException if not a number
  113. }
  114. return result;
  115. }
  116. public static boolean is13VMOrGreater() {
  117. return 1.3 <= vmVersion;
  118. }
  119. public static boolean is14VMOrGreater() {
  120. return 1.4 <= vmVersion;
  121. }
  122. public static boolean is15VMOrGreater() {
  123. return 1.5 <= vmVersion;
  124. }
  125. public static boolean is16VMOrGreater() {
  126. return 1.6 <= vmVersion;
  127. }
  128. public static boolean is17VMOrGreater() {
  129. return 1.7 <= vmVersion;
  130. }
  131. public static boolean is18VMOrGreater() {
  132. return 1.8 <= vmVersion;
  133. }
  134. public static boolean is19VMOrGreater() {
  135. return 9 <= vmVersion;
  136. }
  137. /**
  138. * Shorthand for "if null, throw IllegalArgumentException"
  139. *
  140. * @throws IllegalArgumentException "null {name}" if o is null
  141. */
  142. public static final void throwIaxIfNull(final Object o, final String name) {
  143. if (null == o) {
  144. String message = "null " + (null == name ? "input" : name);
  145. throw new IllegalArgumentException(message);
  146. }
  147. }
  148. /**
  149. * Shorthand for "if not null or not assignable, throw IllegalArgumentException"
  150. *
  151. * @param c the Class to check - use null to ignore type check
  152. * @throws IllegalArgumentException "null {name}" if o is null
  153. */
  154. public static final void throwIaxIfNotAssignable(final Object ra[], final Class<?> c, final String name) {
  155. throwIaxIfNull(ra, name);
  156. String label = (null == name ? "input" : name);
  157. for (int i = 0; i < ra.length; i++) {
  158. if (null == ra[i]) {
  159. String m = " null " + label + "[" + i + "]";
  160. throw new IllegalArgumentException(m);
  161. } else if (null != c) {
  162. Class<?> actualClass = ra[i].getClass();
  163. if (!c.isAssignableFrom(actualClass)) {
  164. String message = label + " not assignable to " + c.getName();
  165. throw new IllegalArgumentException(message);
  166. }
  167. }
  168. }
  169. }
  170. /**
  171. * Shorthand for "if not null or not assignable, throw IllegalArgumentException"
  172. *
  173. * @throws IllegalArgumentException "null {name}" if o is null
  174. */
  175. public static final void throwIaxIfNotAssignable(final Object o, final Class<?> c, final String name) {
  176. throwIaxIfNull(o, name);
  177. if (null != c) {
  178. Class<?> actualClass = o.getClass();
  179. if (!c.isAssignableFrom(actualClass)) {
  180. String message = name + " not assignable to " + c.getName();
  181. throw new IllegalArgumentException(message);
  182. }
  183. }
  184. }
  185. // /**
  186. // * Shorthand for
  187. // "if any not null or not assignable, throw IllegalArgumentException"
  188. // * @throws IllegalArgumentException "{name} is not assignable to {c}"
  189. // */
  190. // public static final void throwIaxIfNotAllAssignable(final Collection
  191. // collection,
  192. // final Class c, final String name) {
  193. // throwIaxIfNull(collection, name);
  194. // if (null != c) {
  195. // for (Iterator iter = collection.iterator(); iter.hasNext();) {
  196. // throwIaxIfNotAssignable(iter.next(), c, name);
  197. //
  198. // }
  199. // }
  200. // }
  201. /**
  202. * Shorthand for "if false, throw IllegalArgumentException"
  203. *
  204. * @throws IllegalArgumentException "{message}" if test is false
  205. */
  206. public static final void throwIaxIfFalse(final boolean test, final String message) {
  207. if (!test) {
  208. throw new IllegalArgumentException(message);
  209. }
  210. }
  211. // /** @return ((null == s) || (0 == s.trim().length())); */
  212. // public static boolean isEmptyTrimmed(String s) {
  213. // return ((null == s) || (0 == s.length())
  214. // || (0 == s.trim().length()));
  215. // }
  216. /** @return ((null == s) || (0 == s.length())); */
  217. public static boolean isEmpty(String s) {
  218. return ((null == s) || (0 == s.length()));
  219. }
  220. /** @return ((null == ra) || (0 == ra.length)) */
  221. public static boolean isEmpty(Object[] ra) {
  222. return ((null == ra) || (0 == ra.length));
  223. }
  224. /** @return ((null == ra) || (0 == ra.length)) */
  225. public static boolean isEmpty(byte[] ra) {
  226. return ((null == ra) || (0 == ra.length));
  227. }
  228. /** @return ((null == collection) || (0 == collection.size())) */
  229. public static boolean isEmpty(Collection<?> collection) {
  230. return ((null == collection) || (0 == collection.size()));
  231. }
  232. /** @return ((null == map) || (0 == map.size())) */
  233. public static boolean isEmpty(Map<?,?> map) {
  234. return ((null == map) || (0 == map.size()));
  235. }
  236. /**
  237. * Splits <code>text</code> at whitespace.
  238. *
  239. * @param text <code>String</code> to split.
  240. */
  241. public static String[] split(String text) {
  242. return (String[]) strings(text).toArray(new String[0]);
  243. }
  244. /**
  245. * Splits <code>input</code> at commas, trimming any white space.
  246. *
  247. * @param input <code>String</code> to split.
  248. * @return List of String of elements.
  249. */
  250. public static List<String> commaSplit(String input) {
  251. return anySplit(input, ",");
  252. }
  253. /**
  254. * Split string as classpath, delimited at File.pathSeparator. Entries are not trimmed, but empty entries are ignored.
  255. *
  256. * @param classpath the String to split - may be null or empty
  257. * @return String[] of classpath entries
  258. */
  259. public static String[] splitClasspath(String classpath) {
  260. if (LangUtil.isEmpty(classpath)) {
  261. return new String[0];
  262. }
  263. StringTokenizer st = new StringTokenizer(classpath, File.pathSeparator);
  264. ArrayList<String> result = new ArrayList<String>(st.countTokens());
  265. while (st.hasMoreTokens()) {
  266. String entry = st.nextToken();
  267. if (!LangUtil.isEmpty(entry)) {
  268. result.add(entry);
  269. }
  270. }
  271. return (String[]) result.toArray(new String[0]);
  272. }
  273. /**
  274. * Get System property as boolean, but use default value where the system property is not set.
  275. *
  276. * @return true if value is set to true, false otherwise
  277. */
  278. public static boolean getBoolean(String propertyName, boolean defaultValue) {
  279. if (null != propertyName) {
  280. try {
  281. String value = System.getProperty(propertyName);
  282. if (null != value) {
  283. return Boolean.valueOf(value).booleanValue();
  284. }
  285. } catch (Throwable t) {
  286. // default below
  287. }
  288. }
  289. return defaultValue;
  290. }
  291. /**
  292. * Splits <code>input</code>, removing delimiter and trimming any white space. Returns an empty collection if the input is null.
  293. * If delimiter is null or empty or if the input contains no delimiters, the input itself is returned after trimming white
  294. * space.
  295. *
  296. * @param input <code>String</code> to split.
  297. * @param delim <code>String</code> separators for input.
  298. * @return List of String of elements.
  299. */
  300. public static List<String> anySplit(String input, String delim) {
  301. if (null == input) {
  302. return Collections.emptyList();
  303. }
  304. ArrayList<String> result = new ArrayList<String>();
  305. if (LangUtil.isEmpty(delim) || (-1 == input.indexOf(delim))) {
  306. result.add(input.trim());
  307. } else {
  308. StringTokenizer st = new StringTokenizer(input, delim);
  309. while (st.hasMoreTokens()) {
  310. result.add(st.nextToken().trim());
  311. }
  312. }
  313. return result;
  314. }
  315. /**
  316. * Splits strings into a <code>List</code> using a <code>StringTokenizer</code>.
  317. *
  318. * @param text <code>String</code> to split.
  319. */
  320. public static List<String> strings(String text) {
  321. if (LangUtil.isEmpty(text)) {
  322. return Collections.emptyList();
  323. }
  324. List<String> strings = new ArrayList<String>();
  325. StringTokenizer tok = new StringTokenizer(text);
  326. while (tok.hasMoreTokens()) {
  327. strings.add(tok.nextToken());
  328. }
  329. return strings;
  330. }
  331. /** @return a non-null unmodifiable List */
  332. public static <T> List<T> safeList(List<T> list) {
  333. return (null == list ? Collections.<T>emptyList() : Collections.unmodifiableList(list));
  334. }
  335. // /**
  336. // * Select from input String[] based on suffix-matching
  337. // * @param inputs String[] of input - null ignored
  338. // * @param suffixes String[] of suffix selectors - null ignored
  339. // * @param ignoreCase if true, ignore case
  340. // * @return String[] of input that end with any input
  341. // */
  342. // public static String[] endsWith(String[] inputs, String[] suffixes,
  343. // boolean ignoreCase) {
  344. // if (LangUtil.isEmpty(inputs) || LangUtil.isEmpty(suffixes)) {
  345. // return new String[0];
  346. // }
  347. // if (ignoreCase) {
  348. // String[] temp = new String[suffixes.length];
  349. // for (int i = 0; i < temp.length; i++) {
  350. // String suff = suffixes[i];
  351. // temp[i] = (null == suff ? null : suff.toLowerCase());
  352. // }
  353. // suffixes = temp;
  354. // }
  355. // ArrayList result = new ArrayList();
  356. // for (int i = 0; i < inputs.length; i++) {
  357. // String input = inputs[i];
  358. // if (null == input) {
  359. // continue;
  360. // }
  361. // if (!ignoreCase) {
  362. // input = input.toLowerCase();
  363. // }
  364. // for (int j = 0; j < suffixes.length; j++) {
  365. // String suffix = suffixes[j];
  366. // if (null == suffix) {
  367. // continue;
  368. // }
  369. // if (input.endsWith(suffix)) {
  370. // result.add(input);
  371. // break;
  372. // }
  373. // }
  374. // }
  375. // return (String[]) result.toArray(new String[0]);
  376. // }
  377. //
  378. // /**
  379. // * Select from input String[] if readable directories
  380. // * @param inputs String[] of input - null ignored
  381. // * @param baseDir the base directory of the input
  382. // * @return String[] of input that end with any input
  383. // */
  384. // public static String[] selectDirectories(String[] inputs, File baseDir) {
  385. // if (LangUtil.isEmpty(inputs)) {
  386. // return new String[0];
  387. // }
  388. // ArrayList result = new ArrayList();
  389. // for (int i = 0; i < inputs.length; i++) {
  390. // String input = inputs[i];
  391. // if (null == input) {
  392. // continue;
  393. // }
  394. // File inputFile = new File(baseDir, input);
  395. // if (inputFile.canRead() && inputFile.isDirectory()) {
  396. // result.add(input);
  397. // }
  398. // }
  399. // return (String[]) result.toArray(new String[0]);
  400. // }
  401. /**
  402. * copy non-null two-dimensional String[][]
  403. *
  404. * @see extractOptions(String[], String[][])
  405. */
  406. public static String[][] copyStrings(String[][] in) {
  407. String[][] out = new String[in.length][];
  408. for (int i = 0; i < out.length; i++) {
  409. out[i] = new String[in[i].length];
  410. System.arraycopy(in[i], 0, out[i], 0, out[i].length);
  411. }
  412. return out;
  413. }
  414. /**
  415. * Extract options and arguments to input option list, returning remainder. The input options will be nullified if not found.
  416. * e.g.,
  417. *
  418. * <pre>
  419. * String[] options = new String[][] { new String[] { &quot;-verbose&quot; }, new String[] { &quot;-classpath&quot;, null } };
  420. * String[] args = extractOptions(args, options);
  421. * boolean verbose = null != options[0][0];
  422. * boolean classpath = options[1][1];
  423. * </pre>
  424. *
  425. * @param args the String[] input options
  426. * @param options the String[][]options to find in the input args - not null for each String[] component the first subcomponent
  427. * is the option itself, and there is one String subcomponent for each additional argument.
  428. * @return String[] of args remaining after extracting options to extracted
  429. */
  430. public static String[] extractOptions(String[] args, String[][] options) {
  431. if (LangUtil.isEmpty(args) || LangUtil.isEmpty(options)) {
  432. return args;
  433. }
  434. BitSet foundSet = new BitSet();
  435. String[] result = new String[args.length];
  436. int resultIndex = 0;
  437. for (int j = 0; j < args.length; j++) {
  438. boolean found = false;
  439. for (int i = 0; !found && (i < options.length); i++) {
  440. String[] option = options[i];
  441. LangUtil.throwIaxIfFalse(!LangUtil.isEmpty(option), "options");
  442. String sought = option[0];
  443. found = sought.equals(args[j]);
  444. if (found) {
  445. foundSet.set(i);
  446. int doMore = option.length - 1;
  447. if (0 < doMore) {
  448. final int MAX = j + doMore;
  449. if (MAX >= args.length) {
  450. String s = "expecting " + doMore + " args after ";
  451. throw new IllegalArgumentException(s + args[j]);
  452. }
  453. for (int k = 1; k < option.length; k++) {
  454. option[k] = args[++j];
  455. }
  456. }
  457. }
  458. }
  459. if (!found) {
  460. result[resultIndex++] = args[j];
  461. }
  462. }
  463. // unset any not found
  464. for (int i = 0; i < options.length; i++) {
  465. if (!foundSet.get(i)) {
  466. options[i][0] = null;
  467. }
  468. }
  469. // fixup remainder
  470. if (resultIndex < args.length) {
  471. String[] temp = new String[resultIndex];
  472. System.arraycopy(result, 0, temp, 0, resultIndex);
  473. args = temp;
  474. }
  475. return args;
  476. }
  477. //
  478. // /**
  479. // * Extract options and arguments to input parameter list, returning
  480. // remainder.
  481. // * @param args the String[] input options
  482. // * @param validOptions the String[] options to find in the input args -
  483. // not null
  484. // * @param optionArgs the int[] number of arguments for each option in
  485. // validOptions
  486. // * (if null, then no arguments for any option)
  487. // * @param extracted the List for the matched options
  488. // * @return String[] of args remaining after extracting options to
  489. // extracted
  490. // */
  491. // public static String[] extractOptions(String[] args, String[]
  492. // validOptions,
  493. // int[] optionArgs, List extracted) {
  494. // if (LangUtil.isEmpty(args)
  495. // || LangUtil.isEmpty(validOptions) ) {
  496. // return args;
  497. // }
  498. // if (null != optionArgs) {
  499. // if (optionArgs.length != validOptions.length) {
  500. // throw new IllegalArgumentException("args must match options");
  501. // }
  502. // }
  503. // String[] result = new String[args.length];
  504. // int resultIndex = 0;
  505. // for (int j = 0; j < args.length; j++) {
  506. // boolean found = false;
  507. // for (int i = 0; !found && (i < validOptions.length); i++) {
  508. // String sought = validOptions[i];
  509. // int doMore = (null == optionArgs ? 0 : optionArgs[i]);
  510. // if (LangUtil.isEmpty(sought)) {
  511. // continue;
  512. // }
  513. // found = sought.equals(args[j]);
  514. // if (found) {
  515. // if (null != extracted) {
  516. // extracted.add(sought);
  517. // }
  518. // if (0 < doMore) {
  519. // final int MAX = j + doMore;
  520. // if (MAX >= args.length) {
  521. // String s = "expecting " + doMore + " args after ";
  522. // throw new IllegalArgumentException(s + args[j]);
  523. // }
  524. // if (null != extracted) {
  525. // while (j < MAX) {
  526. // extracted.add(args[++j]);
  527. // }
  528. // } else {
  529. // j = MAX;
  530. // }
  531. // }
  532. // break;
  533. // }
  534. // }
  535. // if (!found) {
  536. // result[resultIndex++] = args[j];
  537. // }
  538. // }
  539. // if (resultIndex < args.length) {
  540. // String[] temp = new String[resultIndex];
  541. // System.arraycopy(result, 0, temp, 0, resultIndex);
  542. // args = temp;
  543. // }
  544. // return args;
  545. // }
  546. // /** @return String[] of entries in validOptions found in args */
  547. // public static String[] selectOptions(String[] args, String[]
  548. // validOptions) {
  549. // if (LangUtil.isEmpty(args) || LangUtil.isEmpty(validOptions)) {
  550. // return new String[0];
  551. // }
  552. // ArrayList result = new ArrayList();
  553. // for (int i = 0; i < validOptions.length; i++) {
  554. // String sought = validOptions[i];
  555. // if (LangUtil.isEmpty(sought)) {
  556. // continue;
  557. // }
  558. // for (int j = 0; j < args.length; j++) {
  559. // if (sought.equals(args[j])) {
  560. // result.add(sought);
  561. // break;
  562. // }
  563. // }
  564. // }
  565. // return (String[]) result.toArray(new String[0]);
  566. // }
  567. // /** @return String[] of entries in validOptions found in args */
  568. // public static String[] selectOptions(List args, String[] validOptions) {
  569. // if (LangUtil.isEmpty(args) || LangUtil.isEmpty(validOptions)) {
  570. // return new String[0];
  571. // }
  572. // ArrayList result = new ArrayList();
  573. // for (int i = 0; i < validOptions.length; i++) {
  574. // String sought = validOptions[i];
  575. // if (LangUtil.isEmpty(sought)) {
  576. // continue;
  577. // }
  578. // for (Iterator iter = args.iterator(); iter.hasNext();) {
  579. // String arg = (String) iter.next();
  580. // if (sought.equals(arg)) {
  581. // result.add(sought);
  582. // break;
  583. // }
  584. // }
  585. // }
  586. // return (String[]) result.toArray(new String[0]);
  587. // }
  588. // /**
  589. // * Generate variants of String[] options by creating an extra set for
  590. // * each option that ends with "-". If none end with "-", then an
  591. // * array equal to <code>new String[][] { options }</code> is returned;
  592. // * if one ends with "-", then two sets are returned,
  593. // * three causes eight sets, etc.
  594. // * @return String[][] with each option set.
  595. // * @throws IllegalArgumentException if any option is null or empty.
  596. // */
  597. // public static String[][] optionVariants(String[] options) {
  598. // if ((null == options) || (0 == options.length)) {
  599. // return new String[][] { new String[0]};
  600. // }
  601. // // be nice, don't stomp input
  602. // String[] temp = new String[options.length];
  603. // System.arraycopy(options, 0, temp, 0, temp.length);
  604. // options = temp;
  605. // boolean[] dup = new boolean[options.length];
  606. // int numDups = 0;
  607. //
  608. // for (int i = 0; i < options.length; i++) {
  609. // String option = options[i];
  610. // if (LangUtil.isEmpty(option)) {
  611. // throw new IllegalArgumentException("empty option at " + i);
  612. // }
  613. // if (option.endsWith("-")) {
  614. // options[i] = option.substring(0, option.length()-1);
  615. // dup[i] = true;
  616. // numDups++;
  617. // }
  618. // }
  619. // final String[] NONE = new String[0];
  620. // final int variants = exp(2, numDups);
  621. // final String[][] result = new String[variants][];
  622. // // variant is a bitmap wrt doing extra value when dup[k]=true
  623. // for (int variant = 0; variant < variants; variant++) {
  624. // ArrayList next = new ArrayList();
  625. // int nextOption = 0;
  626. // for (int k = 0; k < options.length; k++) {
  627. // if (!dup[k] || (0 != (variant & (1 << (nextOption++))))) {
  628. // next.add(options[k]);
  629. // }
  630. // }
  631. // result[variant] = (String[]) next.toArray(NONE);
  632. // }
  633. // return result;
  634. // }
  635. //
  636. // private static int exp(int base, int power) { // not in Math?
  637. // if (0 > power) {
  638. // throw new IllegalArgumentException("negative power: " + power);
  639. // }
  640. // int result = 1;
  641. // while (0 < power--) {
  642. // result *= base;
  643. // }
  644. // return result;
  645. // }
  646. // /**
  647. // * Make a copy of the array.
  648. // * @return an array with the same component type as source
  649. // * containing same elements, even if null.
  650. // * @throws IllegalArgumentException if source is null
  651. // */
  652. // public static final Object[] copy(Object[] source) {
  653. // LangUtil.throwIaxIfNull(source, "source");
  654. // final Class c = source.getClass().getComponentType();
  655. // Object[] result = (Object[]) Array.newInstance(c, source.length);
  656. // System.arraycopy(source, 0, result, 0, result.length);
  657. // return result;
  658. // }
  659. /**
  660. * Convert arrays safely. The number of elements in the result will be 1 smaller for each element that is null or not
  661. * assignable. This will use sink if it has exactly the right size. The result will always have the same component type as sink.
  662. *
  663. * @return an array with the same component type as sink containing any assignable elements in source (in the same order).
  664. * @throws IllegalArgumentException if either is null
  665. */
  666. public static Object[] safeCopy(Object[] source, Object[] sink) {
  667. final Class<?> sinkType = (null == sink ? Object.class : sink.getClass().getComponentType());
  668. final int sourceLength = (null == source ? 0 : source.length);
  669. final int sinkLength = (null == sink ? 0 : sink.length);
  670. final int resultSize;
  671. ArrayList<Object> result = null;
  672. if (0 == sourceLength) {
  673. resultSize = 0;
  674. } else {
  675. result = new ArrayList<Object>(sourceLength);
  676. for (int i = 0; i < sourceLength; i++) {
  677. if ((null != source[i]) && (sinkType.isAssignableFrom(source[i].getClass()))) {
  678. result.add(source[i]);
  679. }
  680. }
  681. resultSize = result.size();
  682. }
  683. if (resultSize != sinkLength) {
  684. sink = (Object[]) Array.newInstance(sinkType, result.size());
  685. }
  686. if (0 < resultSize) {
  687. sink = result.toArray(sink);
  688. }
  689. return sink;
  690. }
  691. /**
  692. * @return a String with the unqualified class name of the class (or "null")
  693. */
  694. public static String unqualifiedClassName(Class<?> c) {
  695. if (null == c) {
  696. return "null";
  697. }
  698. String name = c.getName();
  699. int loc = name.lastIndexOf(".");
  700. if (-1 != loc) {
  701. name = name.substring(1 + loc);
  702. }
  703. return name;
  704. }
  705. /**
  706. * @return a String with the unqualified class name of the object (or "null")
  707. */
  708. public static String unqualifiedClassName(Object o) {
  709. return LangUtil.unqualifiedClassName(null == o ? null : o.getClass());
  710. }
  711. /** inefficient way to replace all instances of sought with replace */
  712. public static String replace(String in, String sought, String replace) {
  713. if (LangUtil.isEmpty(in) || LangUtil.isEmpty(sought)) {
  714. return in;
  715. }
  716. StringBuffer result = new StringBuffer();
  717. final int len = sought.length();
  718. int start = 0;
  719. int loc;
  720. while (-1 != (loc = in.indexOf(sought, start))) {
  721. result.append(in.substring(start, loc));
  722. if (!LangUtil.isEmpty(replace)) {
  723. result.append(replace);
  724. }
  725. start = loc + len;
  726. }
  727. result.append(in.substring(start));
  728. return result.toString();
  729. }
  730. /** render i right-justified with a given width less than about 40 */
  731. public static String toSizedString(long i, int width) {
  732. String result = "" + i;
  733. int size = result.length();
  734. if (width > size) {
  735. final String pad = " ";
  736. final int padLength = pad.length();
  737. if (width > padLength) {
  738. width = padLength;
  739. }
  740. int topad = width - size;
  741. result = pad.substring(0, topad) + result;
  742. }
  743. return result;
  744. }
  745. // /** clip StringBuffer to maximum number of lines */
  746. // static String clipBuffer(StringBuffer buffer, int maxLines) {
  747. // if ((null == buffer) || (1 > buffer.length())) return "";
  748. // StringBuffer result = new StringBuffer();
  749. // int j = 0;
  750. // final int MAX = maxLines;
  751. // final int N = buffer.length();
  752. // for (int i = 0, srcBegin = 0; i < MAX; srcBegin += j) {
  753. // // todo: replace with String variant if/since getting char?
  754. // char[] chars = new char[128];
  755. // int srcEnd = srcBegin+chars.length;
  756. // if (srcEnd >= N) {
  757. // srcEnd = N-1;
  758. // }
  759. // if (srcBegin == srcEnd) break;
  760. // //log("srcBegin:" + srcBegin + ":srcEnd:" + srcEnd);
  761. // buffer.getChars(srcBegin, srcEnd, chars, 0);
  762. // for (j = 0; j < srcEnd-srcBegin/*chars.length*/; j++) {
  763. // char c = chars[j];
  764. // if (c == '\n') {
  765. // i++;
  766. // j++;
  767. // break;
  768. // }
  769. // }
  770. // try { result.append(chars, 0, j); }
  771. // catch (Throwable t) { }
  772. // }
  773. // return result.toString();
  774. // }
  775. /**
  776. * @return "({UnqualifiedExceptionClass}) {message}"
  777. */
  778. public static String renderExceptionShort(Throwable e) {
  779. if (null == e) {
  780. return "(Throwable) null";
  781. }
  782. return "(" + LangUtil.unqualifiedClassName(e) + ") " + e.getMessage();
  783. }
  784. /**
  785. * Renders exception <code>t</code> after unwrapping and eliding any test packages.
  786. *
  787. * @param t <code>Throwable</code> to print.
  788. * @see #maxStackTrace
  789. */
  790. public static String renderException(Throwable t) {
  791. return renderException(t, true);
  792. }
  793. /**
  794. * Renders exception <code>t</code>, unwrapping, optionally eliding and limiting total number of lines.
  795. *
  796. * @param t <code>Throwable</code> to print.
  797. * @param elide true to limit to 100 lines and elide test packages
  798. * @see StringChecker#TEST_PACKAGES
  799. */
  800. public static String renderException(Throwable t, boolean elide) {
  801. if (null == t) {
  802. return "null throwable";
  803. }
  804. t = unwrapException(t);
  805. StringBuffer stack = stackToString(t, false);
  806. if (elide) {
  807. elideEndingLines(StringChecker.TEST_PACKAGES, stack, 100);
  808. }
  809. return stack.toString();
  810. }
  811. /**
  812. * Trim ending lines from a StringBuffer, clipping to maxLines and further removing any number of trailing lines accepted by
  813. * checker.
  814. *
  815. * @param checker returns true if trailing line should be elided.
  816. * @param stack StringBuffer with lines to elide
  817. * @param maxLines int for maximum number of resulting lines
  818. */
  819. static void elideEndingLines(StringChecker checker, StringBuffer stack, int maxLines) {
  820. if (null == checker || (null == stack) || (0 == stack.length())) {
  821. return;
  822. }
  823. final LinkedList<String> lines = new LinkedList<String>();
  824. StringTokenizer st = new StringTokenizer(stack.toString(), "\n\r");
  825. while (st.hasMoreTokens() && (0 < --maxLines)) {
  826. lines.add(st.nextToken());
  827. }
  828. st = null;
  829. String line;
  830. int elided = 0;
  831. while (!lines.isEmpty()) {
  832. line = (String) lines.getLast();
  833. if (!checker.acceptString(line)) {
  834. break;
  835. } else {
  836. elided++;
  837. lines.removeLast();
  838. }
  839. }
  840. if ((elided > 0) || (maxLines < 1)) {
  841. final int EOL_LEN = EOL.length();
  842. int totalLength = 0;
  843. while (!lines.isEmpty()) {
  844. totalLength += EOL_LEN + ((String) lines.getFirst()).length();
  845. lines.removeFirst();
  846. }
  847. if (stack.length() > totalLength) {
  848. stack.setLength(totalLength);
  849. if (elided > 0) {
  850. stack.append(" (... " + elided + " lines...)");
  851. }
  852. }
  853. }
  854. }
  855. /** Dump message and stack to StringBuffer. */
  856. public static StringBuffer stackToString(Throwable throwable, boolean skipMessage) {
  857. if (null == throwable) {
  858. return new StringBuffer();
  859. }
  860. StringWriter buf = new StringWriter();
  861. PrintWriter writer = new PrintWriter(buf);
  862. if (!skipMessage) {
  863. writer.println(throwable.getMessage());
  864. }
  865. throwable.printStackTrace(writer);
  866. try {
  867. buf.close();
  868. } catch (IOException ioe) {
  869. } // ignored
  870. return buf.getBuffer();
  871. }
  872. /** @return Throwable input or tail of any wrapped exception chain */
  873. public static Throwable unwrapException(Throwable t) {
  874. Throwable current = t;
  875. Throwable next = null;
  876. while (current != null) {
  877. // Java 1.2 exceptions that carry exceptions
  878. if (current instanceof InvocationTargetException) {
  879. next = ((InvocationTargetException) current).getTargetException();
  880. } else if (current instanceof ClassNotFoundException) {
  881. next = ((ClassNotFoundException) current).getException();
  882. } else if (current instanceof ExceptionInInitializerError) {
  883. next = ((ExceptionInInitializerError) current).getException();
  884. } else if (current instanceof PrivilegedActionException) {
  885. next = ((PrivilegedActionException) current).getException();
  886. } else if (current instanceof SQLException) {
  887. next = ((SQLException) current).getNextException();
  888. }
  889. // ...getException():
  890. // javax.naming.event.NamingExceptionEvent
  891. // javax.naming.ldap.UnsolicitedNotification
  892. // javax.xml.parsers.FactoryConfigurationError
  893. // javax.xml.transform.TransformerFactoryConfigurationError
  894. // javax.xml.transform.TransformerException
  895. // org.xml.sax.SAXException
  896. // 1.4: Throwable.getCause
  897. // java.util.logging.LogRecord.getThrown()
  898. if (null == next) {
  899. break;
  900. } else {
  901. current = next;
  902. next = null;
  903. }
  904. }
  905. return current;
  906. }
  907. /**
  908. * Replacement for Arrays.asList(..) which gacks on null and returns a List in which remove is an unsupported operation.
  909. *
  910. * @param array the Object[] to convert (may be null)
  911. * @return the List corresponding to array (never null)
  912. */
  913. public static <T> List<T> arrayAsList(T[] array) {
  914. if ((null == array) || (1 > array.length)) {
  915. return Collections.emptyList();
  916. }
  917. ArrayList<T> list = new ArrayList<T>();
  918. list.addAll(Arrays.asList(array));
  919. return list;
  920. }
  921. /** check if input contains any packages to elide. */
  922. public static class StringChecker {
  923. static StringChecker TEST_PACKAGES = new StringChecker(new String[] { "org.aspectj.testing",
  924. "org.eclipse.jdt.internal.junit", "junit.framework.",
  925. "org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" });
  926. String[] infixes;
  927. /** @param infixes adopted */
  928. StringChecker(String[] infixes) {
  929. this.infixes = infixes;
  930. }
  931. /** @return true if input contains infixes */
  932. public boolean acceptString(String input) {
  933. boolean result = false;
  934. if (!LangUtil.isEmpty(input)) {
  935. for (int i = 0; !result && (i < infixes.length); i++) {
  936. result = (-1 != input.indexOf(infixes[i]));
  937. }
  938. }
  939. return result;
  940. }
  941. }
  942. /**
  943. * Gen classpath.
  944. *
  945. * @param bootclasspath
  946. * @param classpath
  947. * @param classesDir
  948. * @param outputJar
  949. * @return String combining classpath elements
  950. */
  951. public static String makeClasspath( // XXX dumb implementation
  952. String bootclasspath, String classpath, String classesDir, String outputJar) {
  953. StringBuffer sb = new StringBuffer();
  954. addIfNotEmpty(bootclasspath, sb, File.pathSeparator);
  955. addIfNotEmpty(classpath, sb, File.pathSeparator);
  956. if (!addIfNotEmpty(classesDir, sb, File.pathSeparator)) {
  957. addIfNotEmpty(outputJar, sb, File.pathSeparator);
  958. }
  959. return sb.toString();
  960. }
  961. /**
  962. * @param input ignored if null
  963. * @param sink the StringBuffer to add input to - return false if null
  964. * @param delimiter the String to append to input when added - ignored if empty
  965. * @return true if input + delimiter added to sink
  966. */
  967. private static boolean addIfNotEmpty(String input, StringBuffer sink, String delimiter) {
  968. if (LangUtil.isEmpty(input) || (null == sink)) {
  969. return false;
  970. }
  971. sink.append(input);
  972. if (!LangUtil.isEmpty(delimiter)) {
  973. sink.append(delimiter);
  974. }
  975. return true;
  976. }
  977. /**
  978. * Create or initialize a process controller to run a process in another VM asynchronously.
  979. *
  980. * @param controller the ProcessController to initialize, if not null
  981. * @param classpath
  982. * @param mainClass
  983. * @param args
  984. * @return initialized ProcessController
  985. */
  986. public static ProcessController makeProcess(ProcessController controller, String classpath, String mainClass, String[] args) {
  987. File java = LangUtil.getJavaExecutable();
  988. ArrayList<String> cmd = new ArrayList<String>();
  989. cmd.add(java.getAbsolutePath());
  990. cmd.add("-classpath");
  991. cmd.add(classpath);
  992. cmd.add(mainClass);
  993. if (!LangUtil.isEmpty(args)) {
  994. cmd.addAll(Arrays.asList(args));
  995. }
  996. String[] command = (String[]) cmd.toArray(new String[0]);
  997. if (null == controller) {
  998. controller = new ProcessController();
  999. }
  1000. controller.init(command, mainClass);
  1001. return controller;
  1002. }
  1003. // /**
  1004. // * Create a process to run asynchronously.
  1005. // * @param controller if not null, initialize this one
  1006. // * @param command the String[] command to run
  1007. // * @param controller the ProcessControl for streams and results
  1008. // */
  1009. // public static ProcessController makeProcess( // not needed?
  1010. // ProcessController controller,
  1011. // String[] command,
  1012. // String label) {
  1013. // if (null == controller) {
  1014. // controller = new ProcessController();
  1015. // }
  1016. // controller.init(command, label);
  1017. // return controller;
  1018. // }
  1019. /**
  1020. * Find java executable File path from java.home system property.
  1021. *
  1022. * @return File associated with the java command, or null if not found.
  1023. */
  1024. public static File getJavaExecutable() {
  1025. String javaHome = null;
  1026. File result = null;
  1027. // java.home
  1028. // java.class.path
  1029. // java.ext.dirs
  1030. try {
  1031. javaHome = System.getProperty("java.home");
  1032. } catch (Throwable t) {
  1033. // ignore
  1034. }
  1035. if (null != javaHome) {
  1036. File binDir = new File(javaHome, "bin");
  1037. if (binDir.isDirectory() && binDir.canRead()) {
  1038. String[] execs = new String[] { "java", "java.exe" };
  1039. for (int i = 0; i < execs.length; i++) {
  1040. result = new File(binDir, execs[i]);
  1041. if (result.canRead()) {
  1042. break;
  1043. }
  1044. }
  1045. }
  1046. }
  1047. return result;
  1048. }
  1049. // /**
  1050. // * Sleep for a particular period (in milliseconds).
  1051. // *
  1052. // * @param time the long time in milliseconds to sleep
  1053. // * @return true if delay succeeded, false if interrupted 100 times
  1054. // */
  1055. // public static boolean sleep(long milliseconds) {
  1056. // if (milliseconds == 0) {
  1057. // return true;
  1058. // } else if (milliseconds < 0) {
  1059. // throw new IllegalArgumentException("negative: " + milliseconds);
  1060. // }
  1061. // return sleepUntil(milliseconds + System.currentTimeMillis());
  1062. // }
  1063. /**
  1064. * Sleep until a particular time.
  1065. *
  1066. * @param time the long time in milliseconds to sleep until
  1067. * @return true if delay succeeded, false if interrupted 100 times
  1068. */
  1069. public static boolean sleepUntil(long time) {
  1070. if (time == 0) {
  1071. return true;
  1072. } else if (time < 0) {
  1073. throw new IllegalArgumentException("negative: " + time);
  1074. }
  1075. // final Thread thread = Thread.currentThread();
  1076. long curTime = System.currentTimeMillis();
  1077. for (int i = 0; (i < 100) && (curTime < time); i++) {
  1078. try {
  1079. Thread.sleep(time - curTime);
  1080. } catch (InterruptedException e) {
  1081. // ignore
  1082. }
  1083. curTime = System.currentTimeMillis();
  1084. }
  1085. return (curTime >= time);
  1086. }
  1087. /**
  1088. * Handle an external process asynchrously. <code>start()</code> launches a main thread to wait for the process and pipes
  1089. * streams (in child threads) through to the corresponding streams (e.g., the process System.err to this System.err). This can
  1090. * complete normally, by exception, or on demand by a client. Clients can implement <code>doCompleting(..)</code> to get notice
  1091. * when the process completes.
  1092. * <p>
  1093. * The following sample code creates a process with a completion callback starts it, and some time later retries the process.
  1094. *
  1095. * <pre>
  1096. * LangUtil.ProcessController controller = new LangUtil.ProcessController() {
  1097. * protected void doCompleting(LangUtil.ProcessController.Thrown thrown, int result) {
  1098. * // signal result
  1099. * }
  1100. * };
  1101. * controller.init(new String[] { &quot;java&quot;, &quot;-version&quot; }, &quot;java version&quot;);
  1102. * controller.start();
  1103. * // some time later...
  1104. * // retry...
  1105. * if (!controller.completed()) {
  1106. * controller.stop();
  1107. * controller.reinit();
  1108. * controller.start();
  1109. * }
  1110. * </pre>
  1111. *
  1112. * <u>warning</u>: Currently this does not close the input or output streams, since doing so prevents their use later.
  1113. */
  1114. public static class ProcessController {
  1115. /*
  1116. * XXX not verified thread-safe, but should be. Known problems: - user stops (completed = true) then exception thrown from
  1117. * destroying process (stop() expects !completed) ...
  1118. */
  1119. private String[] command;
  1120. private String[] envp;
  1121. private String label;
  1122. private boolean init;
  1123. private boolean started;
  1124. private boolean completed;
  1125. /** if true, stopped by user when not completed */
  1126. private boolean userStopped;
  1127. private Process process;
  1128. private FileUtil.Pipe errStream;
  1129. private FileUtil.Pipe outStream;
  1130. private FileUtil.Pipe inStream;
  1131. private ByteArrayOutputStream errSnoop;
  1132. private ByteArrayOutputStream outSnoop;
  1133. private int result;
  1134. private Thrown thrown;
  1135. public ProcessController() {
  1136. }
  1137. /**
  1138. * Permit re-running using the same command if this is not started or if completed. Can also call this when done with
  1139. * results to release references associated with results (e.g., stack traces).
  1140. */
  1141. public final void reinit() {
  1142. if (!init) {
  1143. throw new IllegalStateException("must init(..) before reinit()");
  1144. }
  1145. if (started && !completed) {
  1146. throw new IllegalStateException("not completed - do stop()");
  1147. }
  1148. // init everything but command and label
  1149. started = false;
  1150. completed = false;
  1151. result = Integer.MIN_VALUE;
  1152. thrown = null;
  1153. process = null;
  1154. errStream = null;
  1155. outStream = null;
  1156. inStream = null;
  1157. }
  1158. public final void init(String classpath, String mainClass, String[] args) {
  1159. init(LangUtil.getJavaExecutable(), classpath, mainClass, args);
  1160. }
  1161. public final void init(File java, String classpath, String mainClass, String[] args) {
  1162. LangUtil.throwIaxIfNull(java, "java");
  1163. LangUtil.throwIaxIfNull(mainClass, "mainClass");
  1164. LangUtil.throwIaxIfNull(args, "args");
  1165. ArrayList<String> cmd = new ArrayList<String>();
  1166. cmd.add(java.getAbsolutePath());
  1167. cmd.add("-classpath");
  1168. cmd.add(classpath);
  1169. cmd.add(mainClass);
  1170. if (!LangUtil.isEmpty(args)) {
  1171. cmd.addAll(Arrays.asList(args));
  1172. }
  1173. init((String[]) cmd.toArray(new String[0]), mainClass);
  1174. }
  1175. public final void init(String[] command, String label) {
  1176. this.command = (String[]) LangUtil.safeCopy(command, new String[0]);
  1177. if (1 > this.command.length) {
  1178. throw new IllegalArgumentException("empty command");
  1179. }
  1180. this.label = LangUtil.isEmpty(label) ? command[0] : label;
  1181. init = true;
  1182. reinit();
  1183. }
  1184. public final void setEnvp(String[] envp) {
  1185. this.envp = (String[]) LangUtil.safeCopy(envp, new String[0]);
  1186. if (1 > this.envp.length) {
  1187. throw new IllegalArgumentException("empty envp");
  1188. }
  1189. }
  1190. public final void setErrSnoop(ByteArrayOutputStream snoop) {
  1191. errSnoop = snoop;
  1192. if (null != errStream) {
  1193. errStream.setSnoop(errSnoop);
  1194. }
  1195. }
  1196. public final void setOutSnoop(ByteArrayOutputStream snoop) {
  1197. outSnoop = snoop;
  1198. if (null != outStream) {
  1199. outStream.setSnoop(outSnoop);
  1200. }
  1201. }
  1202. /**
  1203. * Start running the process and pipes asynchronously.
  1204. *
  1205. * @return Thread started or null if unable to start thread (results available via <code>getThrown()</code>, etc.)
  1206. */
  1207. public final Thread start() {
  1208. if (!init) {
  1209. throw new IllegalStateException("not initialized");
  1210. }
  1211. synchronized (this) {
  1212. if (started) {
  1213. throw new IllegalStateException("already started");
  1214. }
  1215. started = true;
  1216. }
  1217. try {
  1218. process = Runtime.getRuntime().exec(command);
  1219. } catch (IOException e) {
  1220. stop(e, Integer.MIN_VALUE);
  1221. return null;
  1222. }
  1223. errStream = new FileUtil.Pipe(process.getErrorStream(), System.err);
  1224. if (null != errSnoop) {
  1225. errStream.setSnoop(errSnoop);
  1226. }
  1227. outStream = new FileUtil.Pipe(process.getInputStream(), System.out);
  1228. if (null != outSnoop) {
  1229. outStream.setSnoop(outSnoop);
  1230. }
  1231. inStream = new FileUtil.Pipe(System.in, process.getOutputStream());
  1232. // start 4 threads, process & pipes for in, err, out
  1233. Runnable processRunner = new Runnable() {
  1234. public void run() {
  1235. Throwable thrown = null;
  1236. int result = Integer.MIN_VALUE;
  1237. try {
  1238. // pipe threads are children
  1239. new Thread(errStream).start();
  1240. new Thread(outStream).start();
  1241. new Thread(inStream).start();
  1242. process.waitFor();
  1243. result = process.exitValue();
  1244. } catch (Throwable e) {
  1245. thrown = e;
  1246. } finally {
  1247. stop(thrown, result);
  1248. }
  1249. }
  1250. };
  1251. Thread result = new Thread(processRunner, label);
  1252. result.start();
  1253. return result;
  1254. }
  1255. /**
  1256. * Destroy any process, stop any pipes. This waits for the pipes to clear (reading until no more input is available), but
  1257. * does not wait for the input stream for the pipe to close (i.e., not waiting for end-of-file on input stream).
  1258. */
  1259. public final synchronized void stop() {
  1260. if (completed) {
  1261. return;
  1262. }
  1263. userStopped = true;
  1264. stop(null, Integer.MIN_VALUE);
  1265. }
  1266. public final String[] getCommand() {
  1267. String[] toCopy = command;
  1268. if (LangUtil.isEmpty(toCopy)) {
  1269. return new String[0];
  1270. }
  1271. String[] result = new String[toCopy.length];
  1272. System.arraycopy(toCopy, 0, result, 0, result.length);
  1273. return result;
  1274. }
  1275. public final boolean completed() {
  1276. return completed;
  1277. }
  1278. public final boolean started() {
  1279. return started;
  1280. }
  1281. public final boolean userStopped() {
  1282. return userStopped;
  1283. }
  1284. /**
  1285. * Get any Throwable thrown. Note that the process can complete normally (with a valid return value), at the same time the
  1286. * pipes throw exceptions, and that this may return some exceptions even if the process is not complete.
  1287. *
  1288. * @return null if not complete or Thrown containing exceptions thrown by the process and streams.
  1289. */
  1290. public final Thrown getThrown() { // cache this
  1291. return makeThrown(null);
  1292. }
  1293. public final int getResult() {
  1294. return result;
  1295. }
  1296. /**
  1297. * Subclasses implement this to get synchronous notice of completion. All pipes and processes should be complete at this
  1298. * time. To get the exceptions thrown for the pipes, use <code>getThrown()</code>. If there is an exception, the process
  1299. * completed abruptly (including side-effects of the user halting the process). If <code>userStopped()</code> is true, then
  1300. * some client asked that the process be destroyed using <code>stop()</code>. Otherwise, the result code should be the
  1301. * result value returned by the process.
  1302. *
  1303. * @param thrown same as <code>getThrown().fromProcess</code>.
  1304. * @param result same as <code>getResult()</code>
  1305. * @see getThrown()
  1306. * @see getResult()
  1307. * @see stop()
  1308. */
  1309. protected void doCompleting(Thrown thrown, int result) {
  1310. }
  1311. /**
  1312. * Handle termination (on-demand, abrupt, or normal) by destroying and/or halting process and pipes.
  1313. *
  1314. * @param thrown ignored if null
  1315. * @param result ignored if Integer.MIN_VALUE
  1316. */
  1317. private final synchronized void stop(Throwable thrown, int result) {
  1318. if (completed) {
  1319. throw new IllegalStateException("already completed");
  1320. } else if (null != this.thrown) {
  1321. throw new IllegalStateException("already set thrown: " + thrown);
  1322. }
  1323. // assert null == this.thrown
  1324. this.thrown = makeThrown(thrown);
  1325. if (null != process) {
  1326. process.destroy();
  1327. }
  1328. if (null != inStream) {
  1329. inStream.halt(false, true); // this will block if waiting
  1330. inStream = null;
  1331. }
  1332. if (null != outStream) {
  1333. outStream.halt(true, true);
  1334. outStream = null;
  1335. }
  1336. if (null != errStream) {
  1337. errStream.halt(true, true);
  1338. errStream = null;
  1339. }
  1340. if (Integer.MIN_VALUE != result) {
  1341. this.result = result;
  1342. }
  1343. completed = true;
  1344. doCompleting(this.thrown, result);
  1345. }
  1346. /**
  1347. * Create snapshot of Throwable's thrown.
  1348. *
  1349. * @param thrown ignored if null or if this.thrown is not null
  1350. */
  1351. private final synchronized Thrown makeThrown(Throwable processThrown) {
  1352. if (null != thrown) {
  1353. return thrown;
  1354. }
  1355. return new Thrown(processThrown, (null == outStream ? null : outStream.getThrown()), (null == errStream ? null
  1356. : errStream.getThrown()), (null == inStream ? null : inStream.getThrown()));
  1357. }
  1358. public static class Thrown {
  1359. public final Throwable fromProcess;
  1360. public final Throwable fromErrPipe;
  1361. public final Throwable fromOutPipe;
  1362. public final Throwable fromInPipe;
  1363. /** true only if some Throwable is not null */
  1364. public final boolean thrown;
  1365. private Thrown(Throwable fromProcess, Throwable fromOutPipe, Throwable fromErrPipe, Throwable fromInPipe) {
  1366. this.fromProcess = fromProcess;
  1367. this.fromErrPipe = fromErrPipe;
  1368. this.fromOutPipe = fromOutPipe;
  1369. this.fromInPipe = fromInPipe;
  1370. thrown = ((null != fromProcess) || (null != fromInPipe) || (null != fromOutPipe) || (null != fromErrPipe));
  1371. }
  1372. public String toString() {
  1373. StringBuffer sb = new StringBuffer();
  1374. append(sb, fromProcess, "process");
  1375. append(sb, fromOutPipe, " stdout");
  1376. append(sb, fromErrPipe, " stderr");
  1377. append(sb, fromInPipe, " stdin");
  1378. if (0 == sb.length()) {
  1379. return "Thrown (none)";
  1380. } else {
  1381. return sb.toString();
  1382. }
  1383. }
  1384. private void append(StringBuffer sb, Throwable thrown, String label) {
  1385. if (null != thrown) {
  1386. sb.append("from " + label + ": ");
  1387. sb.append(LangUtil.renderExceptionShort(thrown));
  1388. sb.append(LangUtil.EOL);
  1389. }
  1390. }
  1391. } // class Thrown
  1392. }
  1393. public static String getJrtFsFilePath() {
  1394. return getJavaHome() + File.separator + "lib" + File.separator + JRT_FS;
  1395. }
  1396. public static String getJavaHome() {
  1397. return System.getProperty("java.home");
  1398. }
  1399. }