blob: 3970381254c0551ab55c342033e951e2b5c1bdb2 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present 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 Jampani307a21e2016-09-01 15:49:47 -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.File;
tom53efab52014-10-07 17:43:48 -070024import java.io.IOException;
Madan Jampani27b69c62015-05-15 15:49:02 -070025import java.nio.ByteBuffer;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080026import java.nio.file.FileVisitResult;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080027import java.nio.file.Files;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080028import java.nio.file.Path;
29import java.nio.file.Paths;
30import java.nio.file.SimpleFileVisitor;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080031import java.nio.file.StandardCopyOption;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080032import java.nio.file.attribute.BasicFileAttributes;
Madan Jampani27b69c62015-05-15 15:49:02 -070033import java.util.Arrays;
Brian O'Connore2eac102015-02-12 18:30:22 -080034import java.util.Collection;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070035import java.util.Dictionary;
tom53efab52014-10-07 17:43:48 -070036import java.util.List;
Sho SHIMIZUb5638b82016-02-11 14:55:05 -080037import java.util.Optional;
Thomas Vachuskaadba1522015-06-04 15:08:30 -070038import java.util.Random;
Ray Milkey36992c82015-11-17 13:31:15 -080039import java.util.Set;
Madan Jampani27b69c62015-05-15 15:49:02 -070040import java.util.concurrent.CompletableFuture;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070041import java.util.concurrent.ExecutionException;
Jordan Halterman544d1d52017-04-19 23:45:12 -070042import java.util.concurrent.Executor;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070043import java.util.concurrent.Future;
tom5f38b3a2014-08-27 23:50:54 -070044import java.util.concurrent.ThreadFactory;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070045import java.util.concurrent.TimeUnit;
46import java.util.concurrent.TimeoutException;
Madan Jampani307a21e2016-09-01 15:49:47 -070047import java.util.function.BinaryOperator;
Madan Jampania29c6772015-08-17 13:17:07 -070048import java.util.function.Function;
49import java.util.function.Supplier;
Sho SHIMIZU85803e22016-01-13 21:53:43 -080050import java.util.stream.Collectors;
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -070051import java.util.stream.Stream;
52import java.util.stream.StreamSupport;
tom5f38b3a2014-08-27 23:50:54 -070053
Madan Jampani307a21e2016-09-01 15:49:47 -070054import org.slf4j.Logger;
55
56import com.google.common.base.Charsets;
57import com.google.common.base.Strings;
58import com.google.common.collect.Lists;
59import com.google.common.primitives.UnsignedLongs;
60import com.google.common.util.concurrent.ThreadFactoryBuilder;
Ray Milkey705d9bc2014-11-18 08:19:00 -080061
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080062/**
63 * Miscellaneous utility methods.
64 */
tom5f38b3a2014-08-27 23:50:54 -070065public abstract class Tools {
66
67 private Tools() {
68 }
69
Thomas Vachuska02aeb032015-01-06 22:36:30 -080070 private static final Logger log = getLogger(Tools.class);
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080071
Thomas Vachuskaadba1522015-06-04 15:08:30 -070072 private static Random random = new Random();
73
tom5f38b3a2014-08-27 23:50:54 -070074 /**
75 * Returns a thread factory that produces threads named according to the
76 * supplied name pattern.
77 *
78 * @param pattern name pattern
79 * @return thread factory
80 */
81 public static ThreadFactory namedThreads(String pattern) {
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080082 return new ThreadFactoryBuilder()
83 .setNameFormat(pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080084 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
85 .build();
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080086 }
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080087
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080088 /**
89 * Returns a thread factory that produces threads named according to the
90 * supplied name pattern and from the specified thread-group. The thread
91 * group name is expected to be specified in slash-delimited format, e.g.
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080092 * {@code onos/intent}. The thread names will be produced by converting
93 * the thread group name into dash-delimited format and pre-pended to the
94 * specified pattern.
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080095 *
96 * @param groupName group name in slash-delimited format to indicate hierarchy
97 * @param pattern name pattern
98 * @return thread factory
99 */
100 public static ThreadFactory groupedThreads(String groupName, String pattern) {
Jian Li03e9fb02016-03-01 17:13:54 -0800101 return groupedThreads(groupName, pattern, log);
102 }
103
104 /**
105 * Returns a thread factory that produces threads named according to the
106 * supplied name pattern and from the specified thread-group. The thread
107 * group name is expected to be specified in slash-delimited format, e.g.
108 * {@code onos/intent}. The thread names will be produced by converting
109 * the thread group name into dash-delimited format and pre-pended to the
110 * specified pattern. If a logger is specified, it will use the logger to
111 * print out the exception if it has any.
112 *
113 * @param groupName group name in slash-delimited format to indicate hierarchy
114 * @param pattern name pattern
115 * @param logger logger
116 * @return thread factory
117 */
118 public static ThreadFactory groupedThreads(String groupName, String pattern, Logger logger) {
119 if (logger == null) {
120 return groupedThreads(groupName, pattern);
121 }
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -0800122 return new ThreadFactoryBuilder()
123 .setThreadFactory(groupedThreadFactory(groupName))
Thomas Vachuskac13b90a2015-02-18 18:19:55 -0800124 .setNameFormat(groupName.replace(GroupedThreadFactory.DELIMITER, "-") + "-" + pattern)
Jian Li03e9fb02016-03-01 17:13:54 -0800125 .setUncaughtExceptionHandler((t, e) -> logger.error("Uncaught exception on " + t.getName(), e))
Thomas Vachuska480adad2015-03-06 10:27:09 -0800126 .build();
tom5f38b3a2014-08-27 23:50:54 -0700127 }
128
tom782a7cf2014-09-11 23:58:38 -0700129 /**
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800130 * Returns a thread factory that produces threads with MIN_PRIORITY.
131 *
132 * @param factory backing ThreadFactory
133 * @return thread factory
134 */
135 public static ThreadFactory minPriority(ThreadFactory factory) {
136 return new ThreadFactoryBuilder()
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800137 .setThreadFactory(factory)
138 .setPriority(Thread.MIN_PRIORITY)
139 .build();
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800140 }
141
142 /**
Brian O'Connore2eac102015-02-12 18:30:22 -0800143 * Returns true if the collection is null or is empty.
144 *
145 * @param collection collection to test
146 * @return true if null or empty; false otherwise
147 */
148 public static boolean isNullOrEmpty(Collection collection) {
149 return collection == null || collection.isEmpty();
150 }
151
152 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700153 * Returns the specified item if that item is not null; otherwise throws
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700154 * not found exception.
155 *
156 * @param item item to check
157 * @param message not found message
158 * @param <T> item type
159 * @return item if not null
160 * @throws org.onlab.util.ItemNotFoundException if item is null
161 */
162 public static <T> T nullIsNotFound(T item, String message) {
163 if (item == null) {
164 throw new ItemNotFoundException(message);
165 }
166 return item;
167 }
168
169 /**
Ray Milkey36992c82015-11-17 13:31:15 -0800170 * Returns the specified set if the set is not null and not empty;
171 * otherwise throws a not found exception.
172 *
173 * @param item set to check
174 * @param message not found message
175 * @param <T> Set item type
176 * @return item if not null and not empty
177 * @throws org.onlab.util.ItemNotFoundException if set is null or empty
178 */
179 public static <T> Set<T> emptyIsNotFound(Set<T> item, String message) {
180 if (item == null || item.isEmpty()) {
181 throw new ItemNotFoundException(message);
182 }
183 return item;
184 }
185
186 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700187 * Returns the specified item if that item is not null; otherwise throws
188 * bad argument exception.
189 *
190 * @param item item to check
191 * @param message not found message
192 * @param <T> item type
193 * @return item if not null
194 * @throws IllegalArgumentException if item is null
195 */
196 public static <T> T nullIsIllegal(T item, String message) {
197 if (item == null) {
198 throw new IllegalArgumentException(message);
199 }
200 return item;
201 }
202
203 /**
tom782a7cf2014-09-11 23:58:38 -0700204 * Converts a string from hex to long.
205 *
206 * @param string hex number in string form; sans 0x
207 * @return long value
208 */
209 public static long fromHex(String string) {
210 return UnsignedLongs.parseUnsignedLong(string, 16);
211 }
212
213 /**
214 * Converts a long value to hex string; 16 wide and sans 0x.
215 *
216 * @param value long value
217 * @return hex string
218 */
219 public static String toHex(long value) {
220 return Strings.padStart(UnsignedLongs.toString(value, 16), 16, '0');
221 }
222
223 /**
224 * Converts a long value to hex string; 16 wide and sans 0x.
225 *
226 * @param value long value
227 * @param width string width; zero padded
228 * @return hex string
229 */
230 public static String toHex(long value, int width) {
231 return Strings.padStart(UnsignedLongs.toString(value, 16), width, '0');
232 }
tomf110fff2014-09-26 00:38:18 -0700233
234 /**
Madan Jampanif2f086c2016-01-13 16:15:39 -0800235 * Returns the UTF-8 encoded byte[] representation of a String.
Jian Lidfba7392016-01-22 16:46:58 -0800236 * @param input input string
237 * @return UTF-8 encoded byte array
Madan Jampanif2f086c2016-01-13 16:15:39 -0800238 */
239 public static byte[] getBytesUtf8(String input) {
240 return input.getBytes(Charsets.UTF_8);
241 }
242
243 /**
244 * Returns the String representation of UTF-8 encoded byte[].
Jian Lidfba7392016-01-22 16:46:58 -0800245 * @param input input byte array
246 * @return UTF-8 encoded string
Madan Jampanif2f086c2016-01-13 16:15:39 -0800247 */
248 public static String toStringUtf8(byte[] input) {
249 return new String(input, Charsets.UTF_8);
250 }
251
252 /**
Madan Jampani9eb55d12015-08-14 07:47:56 -0700253 * Returns a copy of the input byte array.
254 *
255 * @param original input
256 * @return copy of original
257 */
258 public static byte[] copyOf(byte[] original) {
259 return Arrays.copyOf(original, original.length);
260 }
261
262 /**
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700263 * Get property as a string value.
264 *
265 * @param properties properties to be looked up
266 * @param propertyName the name of the property to look up
267 * @return value when the propertyName is defined or return null
268 */
269 public static String get(Dictionary<?, ?> properties, String propertyName) {
270 Object v = properties.get(propertyName);
271 String s = (v instanceof String) ? (String) v :
272 v != null ? v.toString() : null;
273 return Strings.isNullOrEmpty(s) ? null : s.trim();
274 }
275
276 /**
Jian Lid9b5f552016-03-11 18:15:31 -0800277 * Get Integer property from the propertyName
278 * Return null if propertyName is not found.
279 *
280 * @param properties properties to be looked up
281 * @param propertyName the name of the property to look up
282 * @return value when the propertyName is defined or return null
283 */
284 public static Integer getIntegerProperty(Dictionary<?, ?> properties,
285 String propertyName) {
286 Integer value;
287 try {
288 String s = get(properties, propertyName);
289 value = Strings.isNullOrEmpty(s) ? null : Integer.valueOf(s);
290 } catch (NumberFormatException | ClassCastException e) {
291 value = null;
292 }
293 return value;
294 }
295
296 /**
297 * Get Integer property from the propertyName
298 * Return default value if propertyName is not found.
299 *
300 * @param properties properties to be looked up
301 * @param propertyName the name of the property to look up
302 * @param defaultValue the default value that to be assigned
303 * @return value when the propertyName is defined or return default value
304 */
305 public static int getIntegerProperty(Dictionary<?, ?> properties,
306 String propertyName,
307 int defaultValue) {
308 try {
309 String s = get(properties, propertyName);
310 return Strings.isNullOrEmpty(s) ? defaultValue : Integer.valueOf(s);
311 } catch (NumberFormatException | ClassCastException e) {
312 return defaultValue;
313 }
314 }
315
316 /**
317 * Check property name is defined and set to true.
318 *
319 * @param properties properties to be looked up
320 * @param propertyName the name of the property to look up
321 * @return value when the propertyName is defined or return null
322 */
323 public static Boolean isPropertyEnabled(Dictionary<?, ?> properties,
324 String propertyName) {
325 Boolean value;
326 try {
327 String s = get(properties, propertyName);
328 value = Strings.isNullOrEmpty(s) ? null : Boolean.valueOf(s);
329 } catch (ClassCastException e) {
330 value = null;
331 }
332 return value;
333 }
334
335 /**
336 * Check property name is defined as set to true.
337 *
338 * @param properties properties to be looked up
339 * @param propertyName the name of the property to look up
340 * @param defaultValue the default value that to be assigned
341 * @return value when the propertyName is defined or return the default value
342 */
343 public static boolean isPropertyEnabled(Dictionary<?, ?> properties,
344 String propertyName,
345 boolean defaultValue) {
346 try {
347 String s = get(properties, propertyName);
348 return Strings.isNullOrEmpty(s) ? defaultValue : Boolean.valueOf(s);
349 } catch (ClassCastException e) {
350 return defaultValue;
351 }
352 }
353
354 /**
tomf110fff2014-09-26 00:38:18 -0700355 * Suspends the current thread for a specified number of millis.
356 *
357 * @param ms number of millis
358 */
359 public static void delay(int ms) {
360 try {
361 Thread.sleep(ms);
362 } catch (InterruptedException e) {
363 throw new RuntimeException("Interrupted", e);
364 }
365 }
366
tom53efab52014-10-07 17:43:48 -0700367 /**
sdn94b00152016-08-30 02:12:32 -0700368 * Get Long property from the propertyName
369 * Return null if propertyName is not found.
370 *
371 * @param properties properties to be looked up
372 * @param propertyName the name of the property to look up
373 * @return value when the propertyName is defined or return null
374 */
375 public static Long getLongProperty(Dictionary<?, ?> properties,
376 String propertyName) {
377 Long value;
378 try {
379 String s = get(properties, propertyName);
380 value = Strings.isNullOrEmpty(s) ? null : Long.valueOf(s);
381 } catch (NumberFormatException | ClassCastException e) {
382 value = null;
383 }
384 return value;
385 }
386
387 /**
Madan Jampania29c6772015-08-17 13:17:07 -0700388 * Returns a function that retries execution on failure.
389 * @param base base function
390 * @param exceptionClass type of exception for which to retry
391 * @param maxRetries max number of retries before giving up
392 * @param maxDelayBetweenRetries max delay between successive retries. The actual delay is randomly picked from
393 * the interval (0, maxDelayBetweenRetries]
394 * @return function
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700395 * @param <U> type of function input
396 * @param <V> type of function output
Madan Jampania29c6772015-08-17 13:17:07 -0700397 */
398 public static <U, V> Function<U, V> retryable(Function<U, V> base,
399 Class<? extends Throwable> exceptionClass,
400 int maxRetries,
401 int maxDelayBetweenRetries) {
402 return new RetryingFunction<>(base, exceptionClass, maxRetries, maxDelayBetweenRetries);
403 }
404
405 /**
406 * Returns a Supplier that retries execution on failure.
407 * @param base base supplier
408 * @param exceptionClass type of exception for which to retry
409 * @param maxRetries max number of retries before giving up
410 * @param maxDelayBetweenRetries max delay between successive retries. The actual delay is randomly picked from
411 * the interval (0, maxDelayBetweenRetries]
412 * @return supplier
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700413 * @param <V> type of supplied result
Madan Jampania29c6772015-08-17 13:17:07 -0700414 */
415 public static <V> Supplier<V> retryable(Supplier<V> base,
416 Class<? extends Throwable> exceptionClass,
417 int maxRetries,
418 int maxDelayBetweenRetries) {
419 return () -> new RetryingFunction<>(v -> base.get(),
420 exceptionClass,
421 maxRetries,
422 maxDelayBetweenRetries).apply(null);
423 }
424
425 /**
Thomas Vachuskaadba1522015-06-04 15:08:30 -0700426 * Suspends the current thread for a random number of millis between 0 and
427 * the indicated limit.
428 *
429 * @param ms max number of millis
430 */
431 public static void randomDelay(int ms) {
432 try {
433 Thread.sleep(random.nextInt(ms));
434 } catch (InterruptedException e) {
435 throw new RuntimeException("Interrupted", e);
436 }
437 }
438
439 /**
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700440 * Suspends the current thread for a specified number of millis and nanos.
441 *
442 * @param ms number of millis
443 * @param nanos number of nanos
444 */
445 public static void delay(int ms, int nanos) {
446 try {
447 Thread.sleep(ms, nanos);
448 } catch (InterruptedException e) {
449 throw new RuntimeException("Interrupted", e);
450 }
451 }
452
453 /**
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800454 * Purges the specified directory path.&nbsp;Use with great caution since
455 * no attempt is made to check for symbolic links, which could result in
456 * deletion of unintended files.
457 *
458 * @param path directory to be removed
459 * @throws java.io.IOException if unable to remove contents
460 */
461 public static void removeDirectory(String path) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800462 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700463 File dir = new File(path);
464 if (dir.exists() && dir.isDirectory()) {
465 walkFileTree(Paths.get(path), visitor);
466 if (visitor.exception != null) {
467 throw visitor.exception;
468 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800469 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800470 }
471
472 /**
473 * Purges the specified directory path.&nbsp;Use with great caution since
474 * no attempt is made to check for symbolic links, which could result in
475 * deletion of unintended files.
476 *
477 * @param dir directory to be removed
478 * @throws java.io.IOException if unable to remove contents
479 */
480 public static void removeDirectory(File dir) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800481 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700482 if (dir.exists() && dir.isDirectory()) {
483 walkFileTree(Paths.get(dir.getAbsolutePath()), visitor);
484 if (visitor.exception != null) {
485 throw visitor.exception;
486 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800487 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800488 }
489
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800490 // Auxiliary path visitor for recursive directory structure removal.
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800491 private static class DirectoryDeleter extends SimpleFileVisitor<Path> {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800492
493 private IOException exception;
494
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800495 @Override
496 public FileVisitResult visitFile(Path file, BasicFileAttributes attributes)
497 throws IOException {
498 if (attributes.isRegularFile()) {
499 delete(file);
500 }
501 return FileVisitResult.CONTINUE;
502 }
503
504 @Override
505 public FileVisitResult postVisitDirectory(Path directory, IOException ioe)
506 throws IOException {
507 delete(directory);
508 return FileVisitResult.CONTINUE;
509 }
510
511 @Override
512 public FileVisitResult visitFileFailed(Path file, IOException ioe)
513 throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800514 this.exception = ioe;
515 return FileVisitResult.TERMINATE;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800516 }
517 }
518
Madan Jampani30a57f82015-03-02 12:19:41 -0800519 /**
520 * Returns a human friendly time ago string for a specified system time.
Thomas Vachuska480adad2015-03-06 10:27:09 -0800521 *
Madan Jampani30a57f82015-03-02 12:19:41 -0800522 * @param unixTime system time in millis
523 * @return human friendly time ago
524 */
525 public static String timeAgo(long unixTime) {
526 long deltaMillis = System.currentTimeMillis() - unixTime;
527 long secondsSince = (long) (deltaMillis / 1000.0);
528 long minsSince = (long) (deltaMillis / (1000.0 * 60));
529 long hoursSince = (long) (deltaMillis / (1000.0 * 60 * 60));
530 long daysSince = (long) (deltaMillis / (1000.0 * 60 * 60 * 24));
531 if (daysSince > 0) {
532 return String.format("%dd ago", daysSince);
533 } else if (hoursSince > 0) {
534 return String.format("%dh ago", hoursSince);
535 } else if (minsSince > 0) {
536 return String.format("%dm ago", minsSince);
537 } else if (secondsSince > 0) {
538 return String.format("%ds ago", secondsSince);
539 } else {
540 return "just now";
541 }
542 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800543
544 /**
545 * Copies the specified directory path.&nbsp;Use with great caution since
546 * no attempt is made to check for symbolic links, which could result in
547 * copy of unintended files.
548 *
549 * @param src directory to be copied
550 * @param dst destination directory to be removed
551 * @throws java.io.IOException if unable to remove contents
552 */
553 public static void copyDirectory(String src, String dst) throws IOException {
554 walkFileTree(Paths.get(src), new DirectoryCopier(src, dst));
555 }
556
557 /**
558 * Copies the specified directory path.&nbsp;Use with great caution since
559 * no attempt is made to check for symbolic links, which could result in
560 * copy of unintended files.
561 *
562 * @param src directory to be copied
563 * @param dst destination directory to be removed
564 * @throws java.io.IOException if unable to remove contents
565 */
566 public static void copyDirectory(File src, File dst) throws IOException {
567 walkFileTree(Paths.get(src.getAbsolutePath()),
568 new DirectoryCopier(src.getAbsolutePath(),
569 dst.getAbsolutePath()));
570 }
571
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700572 /**
573 * Returns the future value when complete or if future
574 * completes exceptionally returns the defaultValue.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700575 *
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700576 * @param future future
577 * @param defaultValue default value
578 * @param <T> future value type
579 * @return future value when complete or if future
580 * completes exceptionally returns the defaultValue.
581 */
582 public static <T> T futureGetOrElse(Future<T> future, T defaultValue) {
583 try {
584 return future.get();
585 } catch (InterruptedException e) {
586 Thread.currentThread().interrupt();
587 return defaultValue;
588 } catch (ExecutionException e) {
589 return defaultValue;
590 }
591 }
592
593 /**
594 * Returns the future value when complete or if future
595 * completes exceptionally returns the defaultValue.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700596 *
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700597 * @param future future
598 * @param timeout time to wait for successful completion
599 * @param timeUnit time unit
600 * @param defaultValue default value
601 * @param <T> future value type
602 * @return future value when complete or if future
603 * completes exceptionally returns the defaultValue.
604 */
605 public static <T> T futureGetOrElse(Future<T> future,
606 long timeout,
607 TimeUnit timeUnit,
608 T defaultValue) {
609 try {
610 return future.get(timeout, timeUnit);
611 } catch (InterruptedException e) {
612 Thread.currentThread().interrupt();
613 return defaultValue;
614 } catch (ExecutionException | TimeoutException e) {
615 return defaultValue;
616 }
617 }
618
Madan Jampani27b69c62015-05-15 15:49:02 -0700619 /**
620 * Returns a future that is completed exceptionally.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700621 *
Madan Jampani27b69c62015-05-15 15:49:02 -0700622 * @param t exception
623 * @param <T> future value type
624 * @return future
625 */
626 public static <T> CompletableFuture<T> exceptionalFuture(Throwable t) {
627 CompletableFuture<T> future = new CompletableFuture<>();
628 future.completeExceptionally(t);
629 return future;
630 }
631
632 /**
Jordan Halterman544d1d52017-04-19 23:45:12 -0700633 * Returns a future that's completed using the given {@link Executor} once the given {@code future} is completed.
634 * <p>
635 * {@link CompletableFuture}'s async methods cannot be relied upon to complete futures on an executor thread. If a
636 * future is completed synchronously, {@code CompletableFuture} async methods will often complete the future on the
637 * current thread, ignoring the provided {@code Executor}. This method ensures a more reliable and consistent thread
638 * model by ensuring that futures are always completed using the provided {@code Executor}.
639 *
640 * @param future the future to convert into an asynchronous future
641 * @param executor the executor with which to complete the returned future
642 * @param <T> future value type
643 * @return a new completable future to be completed using the provided {@code executor} once the provided
644 * {@code future} is complete
645 */
646 public static <T> CompletableFuture<T> asyncFuture(CompletableFuture<T> future, Executor executor) {
647 CompletableFuture<T> newFuture = new CompletableFuture<T>();
648 future.whenComplete((result, error) -> executor.execute(() -> {
649 if (future.isCompletedExceptionally()) {
650 newFuture.completeExceptionally(error);
651 } else {
652 newFuture.complete(result);
653 }
654 }));
655 return newFuture;
656 }
657
658 /**
Sho SHIMIZU85803e22016-01-13 21:53:43 -0800659 * Returns a new CompletableFuture completed with a list of computed values
660 * when all of the given CompletableFuture complete.
661 *
662 * @param futures the CompletableFutures
663 * @param <T> value type of CompletableFuture
664 * @return a new CompletableFuture that is completed when all of the given CompletableFutures complete
665 */
666 public static <T> CompletableFuture<List<T>> allOf(List<CompletableFuture<T>> futures) {
667 return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]))
668 .thenApply(v -> futures.stream()
669 .map(CompletableFuture::join)
670 .collect(Collectors.toList())
671 );
672 }
673
674 /**
Madan Jampani307a21e2016-09-01 15:49:47 -0700675 * Returns a new CompletableFuture completed by reducing a list of computed values
676 * when all of the given CompletableFuture complete.
677 *
678 * @param futures the CompletableFutures
679 * @param reducer reducer for computing the result
680 * @param emptyValue zero value to be returned if the input future list is empty
681 * @param <T> value type of CompletableFuture
682 * @return a new CompletableFuture that is completed when all of the given CompletableFutures complete
683 */
684 public static <T> CompletableFuture<T> allOf(List<CompletableFuture<T>> futures,
685 BinaryOperator<T> reducer,
686 T emptyValue) {
687 return Tools.allOf(futures)
688 .thenApply(resultList -> resultList.stream().reduce(reducer).orElse(emptyValue));
689 }
690
691 /**
692 * Returns a new CompletableFuture completed by with the first positive result from a list of
693 * input CompletableFutures.
694 *
695 * @param futures the input list of CompletableFutures
696 * @param positiveResultMatcher matcher to identify a positive result
697 * @param negativeResult value to complete with if none of the futures complete with a positive result
698 * @param <T> value type of CompletableFuture
699 * @return a new CompletableFuture
700 */
701 public static <T> CompletableFuture<T> firstOf(List<CompletableFuture<T>> futures,
702 Match<T> positiveResultMatcher,
703 T negativeResult) {
704 CompletableFuture<T> responseFuture = new CompletableFuture<>();
705 Tools.allOf(Lists.transform(futures, future -> future.thenAccept(r -> {
706 if (positiveResultMatcher.matches(r)) {
707 responseFuture.complete(r);
708 }
709 }))).whenComplete((r, e) -> {
710 if (!responseFuture.isDone()) {
711 if (e != null) {
712 responseFuture.completeExceptionally(e);
713 } else {
714 responseFuture.complete(negativeResult);
715 }
716 }
717 });
718 return responseFuture;
719 }
720
721 /**
Madan Jampani27b69c62015-05-15 15:49:02 -0700722 * Returns the contents of {@code ByteBuffer} as byte array.
723 * <p>
724 * WARNING: There is a performance cost due to array copy
725 * when using this method.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700726 *
Madan Jampani27b69c62015-05-15 15:49:02 -0700727 * @param buffer byte buffer
728 * @return byte array containing the byte buffer contents
729 */
730 public static byte[] byteBuffertoArray(ByteBuffer buffer) {
731 int length = buffer.remaining();
732 if (buffer.hasArray()) {
733 int offset = buffer.arrayOffset() + buffer.position();
734 return Arrays.copyOfRange(buffer.array(), offset, offset + length);
735 }
736 byte[] bytes = new byte[length];
737 buffer.duplicate().get(bytes);
738 return bytes;
739 }
740
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700741 /**
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700742 * Converts an iterable to a stream.
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700743 *
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700744 * @param it iterable to convert
745 * @param <T> type if item
746 * @return iterable as a stream
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700747 */
748 public static <T> Stream<T> stream(Iterable<T> it) {
749 return StreamSupport.stream(it.spliterator(), false);
750 }
751
Sho SHIMIZUb5638b82016-02-11 14:55:05 -0800752 /**
753 * Converts an optional to a stream.
754 *
755 * @param optional optional to convert
756 * @param <T> type of enclosed value
757 * @return optional as a stream
758 */
Sho SHIMIZU6ac20982016-05-04 09:50:54 -0700759 public static <T> Stream<T> stream(Optional<? extends T> optional) {
HIGUCHI Yuta0bc256f2016-05-06 15:28:26 -0700760 return optional.map(x -> Stream.<T>of(x)).orElse(Stream.empty());
Sho SHIMIZUb5638b82016-02-11 14:55:05 -0800761 }
762
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800763 // Auxiliary path visitor for recursive directory structure copying.
764 private static class DirectoryCopier extends SimpleFileVisitor<Path> {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800765 private Path src;
766 private Path dst;
767 private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING;
768
769 DirectoryCopier(String src, String dst) {
770 this.src = Paths.get(src);
771 this.dst = Paths.get(dst);
772 }
773
774 @Override
775 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
776 Path targetPath = dst.resolve(src.relativize(dir));
777 if (!Files.exists(targetPath)) {
778 Files.createDirectory(targetPath);
779 }
780 return FileVisitResult.CONTINUE;
781 }
782
783 @Override
784 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
785 Files.copy(file, dst.resolve(src.relativize(file)), copyOption);
786 return FileVisitResult.CONTINUE;
787 }
788 }
789
tom5f38b3a2014-08-27 23:50:54 -0700790}