blob: 25c0213eab3a50c9224070be90278eef3bb869da [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;
42import java.util.concurrent.Future;
tom5f38b3a2014-08-27 23:50:54 -070043import java.util.concurrent.ThreadFactory;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070044import java.util.concurrent.TimeUnit;
45import java.util.concurrent.TimeoutException;
Madan Jampani307a21e2016-09-01 15:49:47 -070046import java.util.function.BinaryOperator;
Madan Jampania29c6772015-08-17 13:17:07 -070047import java.util.function.Function;
48import java.util.function.Supplier;
Sho SHIMIZU85803e22016-01-13 21:53:43 -080049import java.util.stream.Collectors;
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -070050import java.util.stream.Stream;
51import java.util.stream.StreamSupport;
tom5f38b3a2014-08-27 23:50:54 -070052
Madan Jampani307a21e2016-09-01 15:49:47 -070053import org.slf4j.Logger;
54
55import com.google.common.base.Charsets;
56import com.google.common.base.Strings;
57import com.google.common.collect.Lists;
58import com.google.common.primitives.UnsignedLongs;
59import com.google.common.util.concurrent.ThreadFactoryBuilder;
Ray Milkey705d9bc2014-11-18 08:19:00 -080060
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080061/**
62 * Miscellaneous utility methods.
63 */
tom5f38b3a2014-08-27 23:50:54 -070064public abstract class Tools {
65
66 private Tools() {
67 }
68
Thomas Vachuska02aeb032015-01-06 22:36:30 -080069 private static final Logger log = getLogger(Tools.class);
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080070
Thomas Vachuskaadba1522015-06-04 15:08:30 -070071 private static Random random = new Random();
72
tom5f38b3a2014-08-27 23:50:54 -070073 /**
74 * Returns a thread factory that produces threads named according to the
75 * supplied name pattern.
76 *
77 * @param pattern name pattern
78 * @return thread factory
79 */
80 public static ThreadFactory namedThreads(String pattern) {
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080081 return new ThreadFactoryBuilder()
82 .setNameFormat(pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080083 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
84 .build();
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080085 }
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080086
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080087 /**
88 * Returns a thread factory that produces threads named according to the
89 * supplied name pattern and from the specified thread-group. The thread
90 * group name is expected to be specified in slash-delimited format, e.g.
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080091 * {@code onos/intent}. The thread names will be produced by converting
92 * the thread group name into dash-delimited format and pre-pended to the
93 * specified pattern.
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080094 *
95 * @param groupName group name in slash-delimited format to indicate hierarchy
96 * @param pattern name pattern
97 * @return thread factory
98 */
99 public static ThreadFactory groupedThreads(String groupName, String pattern) {
Jian Li03e9fb02016-03-01 17:13:54 -0800100 return groupedThreads(groupName, pattern, log);
101 }
102
103 /**
104 * Returns a thread factory that produces threads named according to the
105 * supplied name pattern and from the specified thread-group. The thread
106 * group name is expected to be specified in slash-delimited format, e.g.
107 * {@code onos/intent}. The thread names will be produced by converting
108 * the thread group name into dash-delimited format and pre-pended to the
109 * specified pattern. If a logger is specified, it will use the logger to
110 * print out the exception if it has any.
111 *
112 * @param groupName group name in slash-delimited format to indicate hierarchy
113 * @param pattern name pattern
114 * @param logger logger
115 * @return thread factory
116 */
117 public static ThreadFactory groupedThreads(String groupName, String pattern, Logger logger) {
118 if (logger == null) {
119 return groupedThreads(groupName, pattern);
120 }
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -0800121 return new ThreadFactoryBuilder()
122 .setThreadFactory(groupedThreadFactory(groupName))
Thomas Vachuskac13b90a2015-02-18 18:19:55 -0800123 .setNameFormat(groupName.replace(GroupedThreadFactory.DELIMITER, "-") + "-" + pattern)
Jian Li03e9fb02016-03-01 17:13:54 -0800124 .setUncaughtExceptionHandler((t, e) -> logger.error("Uncaught exception on " + t.getName(), e))
Thomas Vachuska480adad2015-03-06 10:27:09 -0800125 .build();
tom5f38b3a2014-08-27 23:50:54 -0700126 }
127
tom782a7cf2014-09-11 23:58:38 -0700128 /**
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800129 * Returns a thread factory that produces threads with MIN_PRIORITY.
130 *
131 * @param factory backing ThreadFactory
132 * @return thread factory
133 */
134 public static ThreadFactory minPriority(ThreadFactory factory) {
135 return new ThreadFactoryBuilder()
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800136 .setThreadFactory(factory)
137 .setPriority(Thread.MIN_PRIORITY)
138 .build();
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800139 }
140
141 /**
Brian O'Connore2eac102015-02-12 18:30:22 -0800142 * Returns true if the collection is null or is empty.
143 *
144 * @param collection collection to test
145 * @return true if null or empty; false otherwise
146 */
147 public static boolean isNullOrEmpty(Collection collection) {
148 return collection == null || collection.isEmpty();
149 }
150
151 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700152 * Returns the specified item if that item is not null; otherwise throws
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700153 * not found exception.
154 *
155 * @param item item to check
156 * @param message not found message
157 * @param <T> item type
158 * @return item if not null
159 * @throws org.onlab.util.ItemNotFoundException if item is null
160 */
161 public static <T> T nullIsNotFound(T item, String message) {
162 if (item == null) {
163 throw new ItemNotFoundException(message);
164 }
165 return item;
166 }
167
168 /**
Ray Milkey36992c82015-11-17 13:31:15 -0800169 * Returns the specified set if the set is not null and not empty;
170 * otherwise throws a not found exception.
171 *
172 * @param item set to check
173 * @param message not found message
174 * @param <T> Set item type
175 * @return item if not null and not empty
176 * @throws org.onlab.util.ItemNotFoundException if set is null or empty
177 */
178 public static <T> Set<T> emptyIsNotFound(Set<T> item, String message) {
179 if (item == null || item.isEmpty()) {
180 throw new ItemNotFoundException(message);
181 }
182 return item;
183 }
184
185 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700186 * Returns the specified item if that item is not null; otherwise throws
187 * bad argument exception.
188 *
189 * @param item item to check
190 * @param message not found message
191 * @param <T> item type
192 * @return item if not null
193 * @throws IllegalArgumentException if item is null
194 */
195 public static <T> T nullIsIllegal(T item, String message) {
196 if (item == null) {
197 throw new IllegalArgumentException(message);
198 }
199 return item;
200 }
201
202 /**
tom782a7cf2014-09-11 23:58:38 -0700203 * Converts a string from hex to long.
204 *
205 * @param string hex number in string form; sans 0x
206 * @return long value
207 */
208 public static long fromHex(String string) {
209 return UnsignedLongs.parseUnsignedLong(string, 16);
210 }
211
212 /**
213 * Converts a long value to hex string; 16 wide and sans 0x.
214 *
215 * @param value long value
216 * @return hex string
217 */
218 public static String toHex(long value) {
219 return Strings.padStart(UnsignedLongs.toString(value, 16), 16, '0');
220 }
221
222 /**
223 * Converts a long value to hex string; 16 wide and sans 0x.
224 *
225 * @param value long value
226 * @param width string width; zero padded
227 * @return hex string
228 */
229 public static String toHex(long value, int width) {
230 return Strings.padStart(UnsignedLongs.toString(value, 16), width, '0');
231 }
tomf110fff2014-09-26 00:38:18 -0700232
233 /**
Madan Jampanif2f086c2016-01-13 16:15:39 -0800234 * Returns the UTF-8 encoded byte[] representation of a String.
Jian Lidfba7392016-01-22 16:46:58 -0800235 * @param input input string
236 * @return UTF-8 encoded byte array
Madan Jampanif2f086c2016-01-13 16:15:39 -0800237 */
238 public static byte[] getBytesUtf8(String input) {
239 return input.getBytes(Charsets.UTF_8);
240 }
241
242 /**
243 * Returns the String representation of UTF-8 encoded byte[].
Jian Lidfba7392016-01-22 16:46:58 -0800244 * @param input input byte array
245 * @return UTF-8 encoded string
Madan Jampanif2f086c2016-01-13 16:15:39 -0800246 */
247 public static String toStringUtf8(byte[] input) {
248 return new String(input, Charsets.UTF_8);
249 }
250
251 /**
Madan Jampani9eb55d12015-08-14 07:47:56 -0700252 * Returns a copy of the input byte array.
253 *
254 * @param original input
255 * @return copy of original
256 */
257 public static byte[] copyOf(byte[] original) {
258 return Arrays.copyOf(original, original.length);
259 }
260
261 /**
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700262 * Get property as a string value.
263 *
264 * @param properties properties to be looked up
265 * @param propertyName the name of the property to look up
266 * @return value when the propertyName is defined or return null
267 */
268 public static String get(Dictionary<?, ?> properties, String propertyName) {
269 Object v = properties.get(propertyName);
270 String s = (v instanceof String) ? (String) v :
271 v != null ? v.toString() : null;
272 return Strings.isNullOrEmpty(s) ? null : s.trim();
273 }
274
275 /**
Jian Lid9b5f552016-03-11 18:15:31 -0800276 * Get Integer property from the propertyName
277 * Return null if propertyName is not found.
278 *
279 * @param properties properties to be looked up
280 * @param propertyName the name of the property to look up
281 * @return value when the propertyName is defined or return null
282 */
283 public static Integer getIntegerProperty(Dictionary<?, ?> properties,
284 String propertyName) {
285 Integer value;
286 try {
287 String s = get(properties, propertyName);
288 value = Strings.isNullOrEmpty(s) ? null : Integer.valueOf(s);
289 } catch (NumberFormatException | ClassCastException e) {
290 value = null;
291 }
292 return value;
293 }
294
295 /**
296 * Get Integer property from the propertyName
297 * Return default value if propertyName is not found.
298 *
299 * @param properties properties to be looked up
300 * @param propertyName the name of the property to look up
301 * @param defaultValue the default value that to be assigned
302 * @return value when the propertyName is defined or return default value
303 */
304 public static int getIntegerProperty(Dictionary<?, ?> properties,
305 String propertyName,
306 int defaultValue) {
307 try {
308 String s = get(properties, propertyName);
309 return Strings.isNullOrEmpty(s) ? defaultValue : Integer.valueOf(s);
310 } catch (NumberFormatException | ClassCastException e) {
311 return defaultValue;
312 }
313 }
314
315 /**
316 * Check property name is defined and set to true.
317 *
318 * @param properties properties to be looked up
319 * @param propertyName the name of the property to look up
320 * @return value when the propertyName is defined or return null
321 */
322 public static Boolean isPropertyEnabled(Dictionary<?, ?> properties,
323 String propertyName) {
324 Boolean value;
325 try {
326 String s = get(properties, propertyName);
327 value = Strings.isNullOrEmpty(s) ? null : Boolean.valueOf(s);
328 } catch (ClassCastException e) {
329 value = null;
330 }
331 return value;
332 }
333
334 /**
335 * Check property name is defined as set to true.
336 *
337 * @param properties properties to be looked up
338 * @param propertyName the name of the property to look up
339 * @param defaultValue the default value that to be assigned
340 * @return value when the propertyName is defined or return the default value
341 */
342 public static boolean isPropertyEnabled(Dictionary<?, ?> properties,
343 String propertyName,
344 boolean defaultValue) {
345 try {
346 String s = get(properties, propertyName);
347 return Strings.isNullOrEmpty(s) ? defaultValue : Boolean.valueOf(s);
348 } catch (ClassCastException e) {
349 return defaultValue;
350 }
351 }
352
353 /**
tomf110fff2014-09-26 00:38:18 -0700354 * Suspends the current thread for a specified number of millis.
355 *
356 * @param ms number of millis
357 */
358 public static void delay(int ms) {
359 try {
360 Thread.sleep(ms);
361 } catch (InterruptedException e) {
362 throw new RuntimeException("Interrupted", e);
363 }
364 }
365
tom53efab52014-10-07 17:43:48 -0700366 /**
sdn94b00152016-08-30 02:12:32 -0700367 * Get Long property from the propertyName
368 * Return null if propertyName is not found.
369 *
370 * @param properties properties to be looked up
371 * @param propertyName the name of the property to look up
372 * @return value when the propertyName is defined or return null
373 */
374 public static Long getLongProperty(Dictionary<?, ?> properties,
375 String propertyName) {
376 Long value;
377 try {
378 String s = get(properties, propertyName);
379 value = Strings.isNullOrEmpty(s) ? null : Long.valueOf(s);
380 } catch (NumberFormatException | ClassCastException e) {
381 value = null;
382 }
383 return value;
384 }
385
386 /**
Madan Jampania29c6772015-08-17 13:17:07 -0700387 * Returns a function that retries execution on failure.
388 * @param base base function
389 * @param exceptionClass type of exception for which to retry
390 * @param maxRetries max number of retries before giving up
391 * @param maxDelayBetweenRetries max delay between successive retries. The actual delay is randomly picked from
392 * the interval (0, maxDelayBetweenRetries]
393 * @return function
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700394 * @param <U> type of function input
395 * @param <V> type of function output
Madan Jampania29c6772015-08-17 13:17:07 -0700396 */
397 public static <U, V> Function<U, V> retryable(Function<U, V> base,
398 Class<? extends Throwable> exceptionClass,
399 int maxRetries,
400 int maxDelayBetweenRetries) {
401 return new RetryingFunction<>(base, exceptionClass, maxRetries, maxDelayBetweenRetries);
402 }
403
404 /**
405 * Returns a Supplier that retries execution on failure.
406 * @param base base supplier
407 * @param exceptionClass type of exception for which to retry
408 * @param maxRetries max number of retries before giving up
409 * @param maxDelayBetweenRetries max delay between successive retries. The actual delay is randomly picked from
410 * the interval (0, maxDelayBetweenRetries]
411 * @return supplier
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700412 * @param <V> type of supplied result
Madan Jampania29c6772015-08-17 13:17:07 -0700413 */
414 public static <V> Supplier<V> retryable(Supplier<V> base,
415 Class<? extends Throwable> exceptionClass,
416 int maxRetries,
417 int maxDelayBetweenRetries) {
418 return () -> new RetryingFunction<>(v -> base.get(),
419 exceptionClass,
420 maxRetries,
421 maxDelayBetweenRetries).apply(null);
422 }
423
424 /**
Thomas Vachuskaadba1522015-06-04 15:08:30 -0700425 * Suspends the current thread for a random number of millis between 0 and
426 * the indicated limit.
427 *
428 * @param ms max number of millis
429 */
430 public static void randomDelay(int ms) {
431 try {
432 Thread.sleep(random.nextInt(ms));
433 } catch (InterruptedException e) {
434 throw new RuntimeException("Interrupted", e);
435 }
436 }
437
438 /**
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700439 * Suspends the current thread for a specified number of millis and nanos.
440 *
441 * @param ms number of millis
442 * @param nanos number of nanos
443 */
444 public static void delay(int ms, int nanos) {
445 try {
446 Thread.sleep(ms, nanos);
447 } catch (InterruptedException e) {
448 throw new RuntimeException("Interrupted", e);
449 }
450 }
451
452 /**
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800453 * Purges the specified directory path.&nbsp;Use with great caution since
454 * no attempt is made to check for symbolic links, which could result in
455 * deletion of unintended files.
456 *
457 * @param path directory to be removed
458 * @throws java.io.IOException if unable to remove contents
459 */
460 public static void removeDirectory(String path) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800461 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700462 File dir = new File(path);
463 if (dir.exists() && dir.isDirectory()) {
464 walkFileTree(Paths.get(path), visitor);
465 if (visitor.exception != null) {
466 throw visitor.exception;
467 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800468 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800469 }
470
471 /**
472 * Purges the specified directory path.&nbsp;Use with great caution since
473 * no attempt is made to check for symbolic links, which could result in
474 * deletion of unintended files.
475 *
476 * @param dir directory to be removed
477 * @throws java.io.IOException if unable to remove contents
478 */
479 public static void removeDirectory(File dir) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800480 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700481 if (dir.exists() && dir.isDirectory()) {
482 walkFileTree(Paths.get(dir.getAbsolutePath()), visitor);
483 if (visitor.exception != null) {
484 throw visitor.exception;
485 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800486 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800487 }
488
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800489 // Auxiliary path visitor for recursive directory structure removal.
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800490 private static class DirectoryDeleter extends SimpleFileVisitor<Path> {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800491
492 private IOException exception;
493
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800494 @Override
495 public FileVisitResult visitFile(Path file, BasicFileAttributes attributes)
496 throws IOException {
497 if (attributes.isRegularFile()) {
498 delete(file);
499 }
500 return FileVisitResult.CONTINUE;
501 }
502
503 @Override
504 public FileVisitResult postVisitDirectory(Path directory, IOException ioe)
505 throws IOException {
506 delete(directory);
507 return FileVisitResult.CONTINUE;
508 }
509
510 @Override
511 public FileVisitResult visitFileFailed(Path file, IOException ioe)
512 throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800513 this.exception = ioe;
514 return FileVisitResult.TERMINATE;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800515 }
516 }
517
Madan Jampani30a57f82015-03-02 12:19:41 -0800518 /**
519 * Returns a human friendly time ago string for a specified system time.
Thomas Vachuska480adad2015-03-06 10:27:09 -0800520 *
Madan Jampani30a57f82015-03-02 12:19:41 -0800521 * @param unixTime system time in millis
522 * @return human friendly time ago
523 */
524 public static String timeAgo(long unixTime) {
525 long deltaMillis = System.currentTimeMillis() - unixTime;
526 long secondsSince = (long) (deltaMillis / 1000.0);
527 long minsSince = (long) (deltaMillis / (1000.0 * 60));
528 long hoursSince = (long) (deltaMillis / (1000.0 * 60 * 60));
529 long daysSince = (long) (deltaMillis / (1000.0 * 60 * 60 * 24));
530 if (daysSince > 0) {
531 return String.format("%dd ago", daysSince);
532 } else if (hoursSince > 0) {
533 return String.format("%dh ago", hoursSince);
534 } else if (minsSince > 0) {
535 return String.format("%dm ago", minsSince);
536 } else if (secondsSince > 0) {
537 return String.format("%ds ago", secondsSince);
538 } else {
539 return "just now";
540 }
541 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800542
543 /**
544 * Copies the specified directory path.&nbsp;Use with great caution since
545 * no attempt is made to check for symbolic links, which could result in
546 * copy of unintended files.
547 *
548 * @param src directory to be copied
549 * @param dst destination directory to be removed
550 * @throws java.io.IOException if unable to remove contents
551 */
552 public static void copyDirectory(String src, String dst) throws IOException {
553 walkFileTree(Paths.get(src), new DirectoryCopier(src, dst));
554 }
555
556 /**
557 * Copies the specified directory path.&nbsp;Use with great caution since
558 * no attempt is made to check for symbolic links, which could result in
559 * copy of unintended files.
560 *
561 * @param src directory to be copied
562 * @param dst destination directory to be removed
563 * @throws java.io.IOException if unable to remove contents
564 */
565 public static void copyDirectory(File src, File dst) throws IOException {
566 walkFileTree(Paths.get(src.getAbsolutePath()),
567 new DirectoryCopier(src.getAbsolutePath(),
568 dst.getAbsolutePath()));
569 }
570
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700571 /**
572 * Returns the future value when complete or if future
573 * completes exceptionally returns the defaultValue.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700574 *
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700575 * @param future future
576 * @param defaultValue default value
577 * @param <T> future value type
578 * @return future value when complete or if future
579 * completes exceptionally returns the defaultValue.
580 */
581 public static <T> T futureGetOrElse(Future<T> future, T defaultValue) {
582 try {
583 return future.get();
584 } catch (InterruptedException e) {
585 Thread.currentThread().interrupt();
586 return defaultValue;
587 } catch (ExecutionException e) {
588 return defaultValue;
589 }
590 }
591
592 /**
593 * Returns the future value when complete or if future
594 * completes exceptionally returns the defaultValue.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700595 *
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700596 * @param future future
597 * @param timeout time to wait for successful completion
598 * @param timeUnit time unit
599 * @param defaultValue default value
600 * @param <T> future value type
601 * @return future value when complete or if future
602 * completes exceptionally returns the defaultValue.
603 */
604 public static <T> T futureGetOrElse(Future<T> future,
605 long timeout,
606 TimeUnit timeUnit,
607 T defaultValue) {
608 try {
609 return future.get(timeout, timeUnit);
610 } catch (InterruptedException e) {
611 Thread.currentThread().interrupt();
612 return defaultValue;
613 } catch (ExecutionException | TimeoutException e) {
614 return defaultValue;
615 }
616 }
617
Madan Jampani27b69c62015-05-15 15:49:02 -0700618 /**
619 * Returns a future that is completed exceptionally.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700620 *
Madan Jampani27b69c62015-05-15 15:49:02 -0700621 * @param t exception
622 * @param <T> future value type
623 * @return future
624 */
625 public static <T> CompletableFuture<T> exceptionalFuture(Throwable t) {
626 CompletableFuture<T> future = new CompletableFuture<>();
627 future.completeExceptionally(t);
628 return future;
629 }
630
631 /**
Sho SHIMIZU85803e22016-01-13 21:53:43 -0800632 * Returns a new CompletableFuture completed with a list of computed values
633 * when all of the given CompletableFuture complete.
634 *
635 * @param futures the CompletableFutures
636 * @param <T> value type of CompletableFuture
637 * @return a new CompletableFuture that is completed when all of the given CompletableFutures complete
638 */
639 public static <T> CompletableFuture<List<T>> allOf(List<CompletableFuture<T>> futures) {
640 return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]))
641 .thenApply(v -> futures.stream()
642 .map(CompletableFuture::join)
643 .collect(Collectors.toList())
644 );
645 }
646
647 /**
Madan Jampani307a21e2016-09-01 15:49:47 -0700648 * Returns a new CompletableFuture completed by reducing a list of computed values
649 * when all of the given CompletableFuture complete.
650 *
651 * @param futures the CompletableFutures
652 * @param reducer reducer for computing the result
653 * @param emptyValue zero value to be returned if the input future list is empty
654 * @param <T> value type of CompletableFuture
655 * @return a new CompletableFuture that is completed when all of the given CompletableFutures complete
656 */
657 public static <T> CompletableFuture<T> allOf(List<CompletableFuture<T>> futures,
658 BinaryOperator<T> reducer,
659 T emptyValue) {
660 return Tools.allOf(futures)
661 .thenApply(resultList -> resultList.stream().reduce(reducer).orElse(emptyValue));
662 }
663
664 /**
665 * Returns a new CompletableFuture completed by with the first positive result from a list of
666 * input CompletableFutures.
667 *
668 * @param futures the input list of CompletableFutures
669 * @param positiveResultMatcher matcher to identify a positive result
670 * @param negativeResult value to complete with if none of the futures complete with a positive result
671 * @param <T> value type of CompletableFuture
672 * @return a new CompletableFuture
673 */
674 public static <T> CompletableFuture<T> firstOf(List<CompletableFuture<T>> futures,
675 Match<T> positiveResultMatcher,
676 T negativeResult) {
677 CompletableFuture<T> responseFuture = new CompletableFuture<>();
678 Tools.allOf(Lists.transform(futures, future -> future.thenAccept(r -> {
679 if (positiveResultMatcher.matches(r)) {
680 responseFuture.complete(r);
681 }
682 }))).whenComplete((r, e) -> {
683 if (!responseFuture.isDone()) {
684 if (e != null) {
685 responseFuture.completeExceptionally(e);
686 } else {
687 responseFuture.complete(negativeResult);
688 }
689 }
690 });
691 return responseFuture;
692 }
693
694 /**
Madan Jampani27b69c62015-05-15 15:49:02 -0700695 * Returns the contents of {@code ByteBuffer} as byte array.
696 * <p>
697 * WARNING: There is a performance cost due to array copy
698 * when using this method.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700699 *
Madan Jampani27b69c62015-05-15 15:49:02 -0700700 * @param buffer byte buffer
701 * @return byte array containing the byte buffer contents
702 */
703 public static byte[] byteBuffertoArray(ByteBuffer buffer) {
704 int length = buffer.remaining();
705 if (buffer.hasArray()) {
706 int offset = buffer.arrayOffset() + buffer.position();
707 return Arrays.copyOfRange(buffer.array(), offset, offset + length);
708 }
709 byte[] bytes = new byte[length];
710 buffer.duplicate().get(bytes);
711 return bytes;
712 }
713
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700714 /**
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700715 * Converts an iterable to a stream.
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700716 *
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700717 * @param it iterable to convert
718 * @param <T> type if item
719 * @return iterable as a stream
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700720 */
721 public static <T> Stream<T> stream(Iterable<T> it) {
722 return StreamSupport.stream(it.spliterator(), false);
723 }
724
Sho SHIMIZUb5638b82016-02-11 14:55:05 -0800725 /**
726 * Converts an optional to a stream.
727 *
728 * @param optional optional to convert
729 * @param <T> type of enclosed value
730 * @return optional as a stream
731 */
Sho SHIMIZU6ac20982016-05-04 09:50:54 -0700732 public static <T> Stream<T> stream(Optional<? extends T> optional) {
HIGUCHI Yuta0bc256f2016-05-06 15:28:26 -0700733 return optional.map(x -> Stream.<T>of(x)).orElse(Stream.empty());
Sho SHIMIZUb5638b82016-02-11 14:55:05 -0800734 }
735
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800736 // Auxiliary path visitor for recursive directory structure copying.
737 private static class DirectoryCopier extends SimpleFileVisitor<Path> {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800738 private Path src;
739 private Path dst;
740 private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING;
741
742 DirectoryCopier(String src, String dst) {
743 this.src = Paths.get(src);
744 this.dst = Paths.get(dst);
745 }
746
747 @Override
748 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
749 Path targetPath = dst.resolve(src.relativize(dir));
750 if (!Files.exists(targetPath)) {
751 Files.createDirectory(targetPath);
752 }
753 return FileVisitResult.CONTINUE;
754 }
755
756 @Override
757 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
758 Files.copy(file, dst.resolve(src.relativize(file)), copyOption);
759 return FileVisitResult.CONTINUE;
760 }
761 }
762
tom5f38b3a2014-08-27 23:50:54 -0700763}