blob: 89bc30009123174c5d7cdb3283c868c5f9fece5a [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska24c849c2014-10-27 09:53:05 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska24c849c2014-10-27 09:53:05 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska24c849c2014-10-27 09:53:05 -070015 */
tom5f38b3a2014-08-27 23:50:54 -070016package org.onlab.util;
17
Madan Jampani2bfa94c2015-04-11 05:03:49 -070018import static java.nio.file.Files.delete;
19import static java.nio.file.Files.walkFileTree;
20import static org.onlab.util.GroupedThreadFactory.groupedThreadFactory;
21import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080022
tom53efab52014-10-07 17:43:48 -070023import java.io.BufferedReader;
24import java.io.File;
Ray Milkey705d9bc2014-11-18 08:19:00 -080025import java.io.FileInputStream;
tom53efab52014-10-07 17:43:48 -070026import java.io.IOException;
Ray Milkey705d9bc2014-11-18 08:19:00 -080027import java.io.InputStreamReader;
Madan Jampani27b69c62015-05-15 15:49:02 -070028import java.nio.ByteBuffer;
Ray Milkey705d9bc2014-11-18 08:19:00 -080029import java.nio.charset.StandardCharsets;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080030import java.nio.file.FileVisitResult;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080031import java.nio.file.Files;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080032import java.nio.file.Path;
33import java.nio.file.Paths;
34import java.nio.file.SimpleFileVisitor;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080035import java.nio.file.StandardCopyOption;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080036import java.nio.file.attribute.BasicFileAttributes;
tom53efab52014-10-07 17:43:48 -070037import java.util.ArrayList;
Madan Jampani27b69c62015-05-15 15:49:02 -070038import java.util.Arrays;
Brian O'Connore2eac102015-02-12 18:30:22 -080039import java.util.Collection;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070040import java.util.Dictionary;
tom53efab52014-10-07 17:43:48 -070041import java.util.List;
Sho SHIMIZUb5638b82016-02-11 14:55:05 -080042import java.util.Optional;
Thomas Vachuskaadba1522015-06-04 15:08:30 -070043import java.util.Random;
Ray Milkey36992c82015-11-17 13:31:15 -080044import java.util.Set;
Madan Jampani27b69c62015-05-15 15:49:02 -070045import java.util.concurrent.CompletableFuture;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070046import java.util.concurrent.ExecutionException;
47import java.util.concurrent.Future;
tom5f38b3a2014-08-27 23:50:54 -070048import java.util.concurrent.ThreadFactory;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070049import java.util.concurrent.TimeUnit;
50import java.util.concurrent.TimeoutException;
Madan Jampania29c6772015-08-17 13:17:07 -070051import java.util.function.Function;
52import java.util.function.Supplier;
Sho SHIMIZU85803e22016-01-13 21:53:43 -080053import java.util.stream.Collectors;
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -070054import java.util.stream.Stream;
55import java.util.stream.StreamSupport;
tom5f38b3a2014-08-27 23:50:54 -070056
Madan Jampani2bfa94c2015-04-11 05:03:49 -070057import org.slf4j.Logger;
58
Madan Jampanif2f086c2016-01-13 16:15:39 -080059import com.google.common.base.Charsets;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070060import com.google.common.base.Strings;
61import com.google.common.primitives.UnsignedLongs;
62import com.google.common.util.concurrent.ThreadFactoryBuilder;
Ray Milkey705d9bc2014-11-18 08:19:00 -080063
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080064/**
65 * Miscellaneous utility methods.
66 */
tom5f38b3a2014-08-27 23:50:54 -070067public abstract class Tools {
68
69 private Tools() {
70 }
71
Thomas Vachuska02aeb032015-01-06 22:36:30 -080072 private static final Logger log = getLogger(Tools.class);
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080073
Thomas Vachuskaadba1522015-06-04 15:08:30 -070074 private static Random random = new Random();
75
tom5f38b3a2014-08-27 23:50:54 -070076 /**
77 * Returns a thread factory that produces threads named according to the
78 * supplied name pattern.
79 *
80 * @param pattern name pattern
81 * @return thread factory
82 */
83 public static ThreadFactory namedThreads(String pattern) {
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080084 return new ThreadFactoryBuilder()
85 .setNameFormat(pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080086 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
87 .build();
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080088 }
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080089
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080090 /**
91 * Returns a thread factory that produces threads named according to the
92 * supplied name pattern and from the specified thread-group. The thread
93 * group name is expected to be specified in slash-delimited format, e.g.
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080094 * {@code onos/intent}. The thread names will be produced by converting
95 * the thread group name into dash-delimited format and pre-pended to the
96 * specified pattern.
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080097 *
98 * @param groupName group name in slash-delimited format to indicate hierarchy
99 * @param pattern name pattern
100 * @return thread factory
101 */
102 public static ThreadFactory groupedThreads(String groupName, String pattern) {
Jian Li03e9fb02016-03-01 17:13:54 -0800103 return groupedThreads(groupName, pattern, log);
104 }
105
106 /**
107 * Returns a thread factory that produces threads named according to the
108 * supplied name pattern and from the specified thread-group. The thread
109 * group name is expected to be specified in slash-delimited format, e.g.
110 * {@code onos/intent}. The thread names will be produced by converting
111 * the thread group name into dash-delimited format and pre-pended to the
112 * specified pattern. If a logger is specified, it will use the logger to
113 * print out the exception if it has any.
114 *
115 * @param groupName group name in slash-delimited format to indicate hierarchy
116 * @param pattern name pattern
117 * @param logger logger
118 * @return thread factory
119 */
120 public static ThreadFactory groupedThreads(String groupName, String pattern, Logger logger) {
121 if (logger == null) {
122 return groupedThreads(groupName, pattern);
123 }
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -0800124 return new ThreadFactoryBuilder()
125 .setThreadFactory(groupedThreadFactory(groupName))
Thomas Vachuskac13b90a2015-02-18 18:19:55 -0800126 .setNameFormat(groupName.replace(GroupedThreadFactory.DELIMITER, "-") + "-" + pattern)
Jian Li03e9fb02016-03-01 17:13:54 -0800127 .setUncaughtExceptionHandler((t, e) -> logger.error("Uncaught exception on " + t.getName(), e))
Thomas Vachuska480adad2015-03-06 10:27:09 -0800128 .build();
tom5f38b3a2014-08-27 23:50:54 -0700129 }
130
tom782a7cf2014-09-11 23:58:38 -0700131 /**
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800132 * Returns a thread factory that produces threads with MIN_PRIORITY.
133 *
134 * @param factory backing ThreadFactory
135 * @return thread factory
136 */
137 public static ThreadFactory minPriority(ThreadFactory factory) {
138 return new ThreadFactoryBuilder()
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800139 .setThreadFactory(factory)
140 .setPriority(Thread.MIN_PRIORITY)
141 .build();
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800142 }
143
144 /**
Brian O'Connore2eac102015-02-12 18:30:22 -0800145 * Returns true if the collection is null or is empty.
146 *
147 * @param collection collection to test
148 * @return true if null or empty; false otherwise
149 */
150 public static boolean isNullOrEmpty(Collection collection) {
151 return collection == null || collection.isEmpty();
152 }
153
154 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700155 * Returns the specified item if that item is not null; otherwise throws
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700156 * not found exception.
157 *
158 * @param item item to check
159 * @param message not found message
160 * @param <T> item type
161 * @return item if not null
162 * @throws org.onlab.util.ItemNotFoundException if item is null
163 */
164 public static <T> T nullIsNotFound(T item, String message) {
165 if (item == null) {
166 throw new ItemNotFoundException(message);
167 }
168 return item;
169 }
170
171 /**
Ray Milkey36992c82015-11-17 13:31:15 -0800172 * Returns the specified set if the set is not null and not empty;
173 * otherwise throws a not found exception.
174 *
175 * @param item set to check
176 * @param message not found message
177 * @param <T> Set item type
178 * @return item if not null and not empty
179 * @throws org.onlab.util.ItemNotFoundException if set is null or empty
180 */
181 public static <T> Set<T> emptyIsNotFound(Set<T> item, String message) {
182 if (item == null || item.isEmpty()) {
183 throw new ItemNotFoundException(message);
184 }
185 return item;
186 }
187
188 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700189 * Returns the specified item if that item is not null; otherwise throws
190 * bad argument exception.
191 *
192 * @param item item to check
193 * @param message not found message
194 * @param <T> item type
195 * @return item if not null
196 * @throws IllegalArgumentException if item is null
197 */
198 public static <T> T nullIsIllegal(T item, String message) {
199 if (item == null) {
200 throw new IllegalArgumentException(message);
201 }
202 return item;
203 }
204
205 /**
tom782a7cf2014-09-11 23:58:38 -0700206 * Converts a string from hex to long.
207 *
208 * @param string hex number in string form; sans 0x
209 * @return long value
210 */
211 public static long fromHex(String string) {
212 return UnsignedLongs.parseUnsignedLong(string, 16);
213 }
214
215 /**
216 * Converts a long value to hex string; 16 wide and sans 0x.
217 *
218 * @param value long value
219 * @return hex string
220 */
221 public static String toHex(long value) {
222 return Strings.padStart(UnsignedLongs.toString(value, 16), 16, '0');
223 }
224
225 /**
226 * Converts a long value to hex string; 16 wide and sans 0x.
227 *
228 * @param value long value
229 * @param width string width; zero padded
230 * @return hex string
231 */
232 public static String toHex(long value, int width) {
233 return Strings.padStart(UnsignedLongs.toString(value, 16), width, '0');
234 }
tomf110fff2014-09-26 00:38:18 -0700235
236 /**
Madan Jampanif2f086c2016-01-13 16:15:39 -0800237 * Returns the UTF-8 encoded byte[] representation of a String.
Jian Lidfba7392016-01-22 16:46:58 -0800238 * @param input input string
239 * @return UTF-8 encoded byte array
Madan Jampanif2f086c2016-01-13 16:15:39 -0800240 */
241 public static byte[] getBytesUtf8(String input) {
242 return input.getBytes(Charsets.UTF_8);
243 }
244
245 /**
246 * Returns the String representation of UTF-8 encoded byte[].
Jian Lidfba7392016-01-22 16:46:58 -0800247 * @param input input byte array
248 * @return UTF-8 encoded string
Madan Jampanif2f086c2016-01-13 16:15:39 -0800249 */
250 public static String toStringUtf8(byte[] input) {
251 return new String(input, Charsets.UTF_8);
252 }
253
254 /**
Madan Jampani9eb55d12015-08-14 07:47:56 -0700255 * Returns a copy of the input byte array.
256 *
257 * @param original input
258 * @return copy of original
259 */
260 public static byte[] copyOf(byte[] original) {
261 return Arrays.copyOf(original, original.length);
262 }
263
264 /**
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700265 * Get property as a string value.
266 *
267 * @param properties properties to be looked up
268 * @param propertyName the name of the property to look up
269 * @return value when the propertyName is defined or return null
270 */
271 public static String get(Dictionary<?, ?> properties, String propertyName) {
272 Object v = properties.get(propertyName);
273 String s = (v instanceof String) ? (String) v :
274 v != null ? v.toString() : null;
275 return Strings.isNullOrEmpty(s) ? null : s.trim();
276 }
277
278 /**
tomf110fff2014-09-26 00:38:18 -0700279 * Suspends the current thread for a specified number of millis.
280 *
281 * @param ms number of millis
282 */
283 public static void delay(int ms) {
284 try {
285 Thread.sleep(ms);
286 } catch (InterruptedException e) {
287 throw new RuntimeException("Interrupted", e);
288 }
289 }
290
tom53efab52014-10-07 17:43:48 -0700291 /**
Madan Jampania29c6772015-08-17 13:17:07 -0700292 * Returns a function that retries execution on failure.
293 * @param base base function
294 * @param exceptionClass type of exception for which to retry
295 * @param maxRetries max number of retries before giving up
296 * @param maxDelayBetweenRetries max delay between successive retries. The actual delay is randomly picked from
297 * the interval (0, maxDelayBetweenRetries]
298 * @return function
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700299 * @param <U> type of function input
300 * @param <V> type of function output
Madan Jampania29c6772015-08-17 13:17:07 -0700301 */
302 public static <U, V> Function<U, V> retryable(Function<U, V> base,
303 Class<? extends Throwable> exceptionClass,
304 int maxRetries,
305 int maxDelayBetweenRetries) {
306 return new RetryingFunction<>(base, exceptionClass, maxRetries, maxDelayBetweenRetries);
307 }
308
309 /**
310 * Returns a Supplier that retries execution on failure.
311 * @param base base supplier
312 * @param exceptionClass type of exception for which to retry
313 * @param maxRetries max number of retries before giving up
314 * @param maxDelayBetweenRetries max delay between successive retries. The actual delay is randomly picked from
315 * the interval (0, maxDelayBetweenRetries]
316 * @return supplier
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700317 * @param <V> type of supplied result
Madan Jampania29c6772015-08-17 13:17:07 -0700318 */
319 public static <V> Supplier<V> retryable(Supplier<V> base,
320 Class<? extends Throwable> exceptionClass,
321 int maxRetries,
322 int maxDelayBetweenRetries) {
323 return () -> new RetryingFunction<>(v -> base.get(),
324 exceptionClass,
325 maxRetries,
326 maxDelayBetweenRetries).apply(null);
327 }
328
329 /**
Thomas Vachuskaadba1522015-06-04 15:08:30 -0700330 * Suspends the current thread for a random number of millis between 0 and
331 * the indicated limit.
332 *
333 * @param ms max number of millis
334 */
335 public static void randomDelay(int ms) {
336 try {
337 Thread.sleep(random.nextInt(ms));
338 } catch (InterruptedException e) {
339 throw new RuntimeException("Interrupted", e);
340 }
341 }
342
343 /**
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700344 * Suspends the current thread for a specified number of millis and nanos.
345 *
346 * @param ms number of millis
347 * @param nanos number of nanos
348 */
349 public static void delay(int ms, int nanos) {
350 try {
351 Thread.sleep(ms, nanos);
352 } catch (InterruptedException e) {
353 throw new RuntimeException("Interrupted", e);
354 }
355 }
356
357 /**
tom53efab52014-10-07 17:43:48 -0700358 * Slurps the contents of a file into a list of strings, one per line.
359 *
360 * @param path file path
361 * @return file contents
HIGUCHI Yuta3b3bd1e2015-09-22 16:39:33 -0700362 * @deprecated in Emu release
tom53efab52014-10-07 17:43:48 -0700363 */
HIGUCHI Yuta3b3bd1e2015-09-22 16:39:33 -0700364 @Deprecated
tom53efab52014-10-07 17:43:48 -0700365 public static List<String> slurp(File path) {
HIGUCHI Yutacf4d6172015-09-22 16:39:33 -0700366 try (
Ray Milkey705d9bc2014-11-18 08:19:00 -0800367 BufferedReader br = new BufferedReader(
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800368 new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8));
HIGUCHI Yutacf4d6172015-09-22 16:39:33 -0700369 ) {
tom53efab52014-10-07 17:43:48 -0700370 List<String> lines = new ArrayList<>();
371 String line;
372 while ((line = br.readLine()) != null) {
373 lines.add(line);
374 }
375 return lines;
376
377 } catch (IOException e) {
378 return null;
379 }
380 }
381
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800382 /**
383 * Purges the specified directory path.&nbsp;Use with great caution since
384 * no attempt is made to check for symbolic links, which could result in
385 * deletion of unintended files.
386 *
387 * @param path directory to be removed
388 * @throws java.io.IOException if unable to remove contents
389 */
390 public static void removeDirectory(String path) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800391 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700392 File dir = new File(path);
393 if (dir.exists() && dir.isDirectory()) {
394 walkFileTree(Paths.get(path), visitor);
395 if (visitor.exception != null) {
396 throw visitor.exception;
397 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800398 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800399 }
400
401 /**
402 * Purges the specified directory path.&nbsp;Use with great caution since
403 * no attempt is made to check for symbolic links, which could result in
404 * deletion of unintended files.
405 *
406 * @param dir directory to be removed
407 * @throws java.io.IOException if unable to remove contents
408 */
409 public static void removeDirectory(File dir) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800410 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700411 if (dir.exists() && dir.isDirectory()) {
412 walkFileTree(Paths.get(dir.getAbsolutePath()), visitor);
413 if (visitor.exception != null) {
414 throw visitor.exception;
415 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800416 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800417 }
418
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800419 // Auxiliary path visitor for recursive directory structure removal.
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800420 private static class DirectoryDeleter extends SimpleFileVisitor<Path> {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800421
422 private IOException exception;
423
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800424 @Override
425 public FileVisitResult visitFile(Path file, BasicFileAttributes attributes)
426 throws IOException {
427 if (attributes.isRegularFile()) {
428 delete(file);
429 }
430 return FileVisitResult.CONTINUE;
431 }
432
433 @Override
434 public FileVisitResult postVisitDirectory(Path directory, IOException ioe)
435 throws IOException {
436 delete(directory);
437 return FileVisitResult.CONTINUE;
438 }
439
440 @Override
441 public FileVisitResult visitFileFailed(Path file, IOException ioe)
442 throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800443 this.exception = ioe;
444 return FileVisitResult.TERMINATE;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800445 }
446 }
447
Madan Jampani30a57f82015-03-02 12:19:41 -0800448 /**
449 * Returns a human friendly time ago string for a specified system time.
Thomas Vachuska480adad2015-03-06 10:27:09 -0800450 *
Madan Jampani30a57f82015-03-02 12:19:41 -0800451 * @param unixTime system time in millis
452 * @return human friendly time ago
453 */
454 public static String timeAgo(long unixTime) {
455 long deltaMillis = System.currentTimeMillis() - unixTime;
456 long secondsSince = (long) (deltaMillis / 1000.0);
457 long minsSince = (long) (deltaMillis / (1000.0 * 60));
458 long hoursSince = (long) (deltaMillis / (1000.0 * 60 * 60));
459 long daysSince = (long) (deltaMillis / (1000.0 * 60 * 60 * 24));
460 if (daysSince > 0) {
461 return String.format("%dd ago", daysSince);
462 } else if (hoursSince > 0) {
463 return String.format("%dh ago", hoursSince);
464 } else if (minsSince > 0) {
465 return String.format("%dm ago", minsSince);
466 } else if (secondsSince > 0) {
467 return String.format("%ds ago", secondsSince);
468 } else {
469 return "just now";
470 }
471 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800472
473 /**
474 * Copies the specified directory path.&nbsp;Use with great caution since
475 * no attempt is made to check for symbolic links, which could result in
476 * copy of unintended files.
477 *
478 * @param src directory to be copied
479 * @param dst destination directory to be removed
480 * @throws java.io.IOException if unable to remove contents
481 */
482 public static void copyDirectory(String src, String dst) throws IOException {
483 walkFileTree(Paths.get(src), new DirectoryCopier(src, dst));
484 }
485
486 /**
487 * Copies the specified directory path.&nbsp;Use with great caution since
488 * no attempt is made to check for symbolic links, which could result in
489 * copy of unintended files.
490 *
491 * @param src directory to be copied
492 * @param dst destination directory to be removed
493 * @throws java.io.IOException if unable to remove contents
494 */
495 public static void copyDirectory(File src, File dst) throws IOException {
496 walkFileTree(Paths.get(src.getAbsolutePath()),
497 new DirectoryCopier(src.getAbsolutePath(),
498 dst.getAbsolutePath()));
499 }
500
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700501 /**
502 * Returns the future value when complete or if future
503 * completes exceptionally returns the defaultValue.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700504 *
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700505 * @param future future
506 * @param defaultValue default value
507 * @param <T> future value type
508 * @return future value when complete or if future
509 * completes exceptionally returns the defaultValue.
510 */
511 public static <T> T futureGetOrElse(Future<T> future, T defaultValue) {
512 try {
513 return future.get();
514 } catch (InterruptedException e) {
515 Thread.currentThread().interrupt();
516 return defaultValue;
517 } catch (ExecutionException e) {
518 return defaultValue;
519 }
520 }
521
522 /**
523 * Returns the future value when complete or if future
524 * completes exceptionally returns the defaultValue.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700525 *
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700526 * @param future future
527 * @param timeout time to wait for successful completion
528 * @param timeUnit time unit
529 * @param defaultValue default value
530 * @param <T> future value type
531 * @return future value when complete or if future
532 * completes exceptionally returns the defaultValue.
533 */
534 public static <T> T futureGetOrElse(Future<T> future,
535 long timeout,
536 TimeUnit timeUnit,
537 T defaultValue) {
538 try {
539 return future.get(timeout, timeUnit);
540 } catch (InterruptedException e) {
541 Thread.currentThread().interrupt();
542 return defaultValue;
543 } catch (ExecutionException | TimeoutException e) {
544 return defaultValue;
545 }
546 }
547
Madan Jampani27b69c62015-05-15 15:49:02 -0700548 /**
549 * Returns a future that is completed exceptionally.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700550 *
Madan Jampani27b69c62015-05-15 15:49:02 -0700551 * @param t exception
552 * @param <T> future value type
553 * @return future
554 */
555 public static <T> CompletableFuture<T> exceptionalFuture(Throwable t) {
556 CompletableFuture<T> future = new CompletableFuture<>();
557 future.completeExceptionally(t);
558 return future;
559 }
560
561 /**
Sho SHIMIZU85803e22016-01-13 21:53:43 -0800562 * Returns a new CompletableFuture completed with a list of computed values
563 * when all of the given CompletableFuture complete.
564 *
565 * @param futures the CompletableFutures
566 * @param <T> value type of CompletableFuture
567 * @return a new CompletableFuture that is completed when all of the given CompletableFutures complete
568 */
569 public static <T> CompletableFuture<List<T>> allOf(List<CompletableFuture<T>> futures) {
570 return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]))
571 .thenApply(v -> futures.stream()
572 .map(CompletableFuture::join)
573 .collect(Collectors.toList())
574 );
575 }
576
577 /**
Madan Jampani27b69c62015-05-15 15:49:02 -0700578 * Returns the contents of {@code ByteBuffer} as byte array.
579 * <p>
580 * WARNING: There is a performance cost due to array copy
581 * when using this method.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700582 *
Madan Jampani27b69c62015-05-15 15:49:02 -0700583 * @param buffer byte buffer
584 * @return byte array containing the byte buffer contents
585 */
586 public static byte[] byteBuffertoArray(ByteBuffer buffer) {
587 int length = buffer.remaining();
588 if (buffer.hasArray()) {
589 int offset = buffer.arrayOffset() + buffer.position();
590 return Arrays.copyOfRange(buffer.array(), offset, offset + length);
591 }
592 byte[] bytes = new byte[length];
593 buffer.duplicate().get(bytes);
594 return bytes;
595 }
596
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700597 /**
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700598 * Converts an iterable to a stream.
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700599 *
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700600 * @param it iterable to convert
601 * @param <T> type if item
602 * @return iterable as a stream
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700603 */
604 public static <T> Stream<T> stream(Iterable<T> it) {
605 return StreamSupport.stream(it.spliterator(), false);
606 }
607
Sho SHIMIZUb5638b82016-02-11 14:55:05 -0800608 /**
609 * Converts an optional to a stream.
610 *
611 * @param optional optional to convert
612 * @param <T> type of enclosed value
613 * @return optional as a stream
614 */
615 public static <T> Stream<T> stream(Optional<T> optional) {
616 return optional.map(Stream::of).orElse(Stream.empty());
617 }
618
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800619 // Auxiliary path visitor for recursive directory structure copying.
620 private static class DirectoryCopier extends SimpleFileVisitor<Path> {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800621 private Path src;
622 private Path dst;
623 private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING;
624
625 DirectoryCopier(String src, String dst) {
626 this.src = Paths.get(src);
627 this.dst = Paths.get(dst);
628 }
629
630 @Override
631 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
632 Path targetPath = dst.resolve(src.relativize(dir));
633 if (!Files.exists(targetPath)) {
634 Files.createDirectory(targetPath);
635 }
636 return FileVisitResult.CONTINUE;
637 }
638
639 @Override
640 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
641 Files.copy(file, dst.resolve(src.relativize(file)), copyOption);
642 return FileVisitResult.CONTINUE;
643 }
644 }
645
tom5f38b3a2014-08-27 23:50:54 -0700646}