blob: 40e59d0695a00ddce240625bea3a3f07367bd685 [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
Jian Li66f15262016-03-03 11:18:40 -080018import com.google.common.base.Charsets;
19import com.google.common.base.Strings;
20import com.google.common.primitives.UnsignedLongs;
21import com.google.common.util.concurrent.ThreadFactoryBuilder;
22import org.slf4j.Logger;
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080023
tom53efab52014-10-07 17:43:48 -070024import java.io.BufferedReader;
25import java.io.File;
Ray Milkey705d9bc2014-11-18 08:19:00 -080026import java.io.FileInputStream;
tom53efab52014-10-07 17:43:48 -070027import java.io.IOException;
Ray Milkey705d9bc2014-11-18 08:19:00 -080028import java.io.InputStreamReader;
Madan Jampani27b69c62015-05-15 15:49:02 -070029import java.nio.ByteBuffer;
Ray Milkey705d9bc2014-11-18 08:19:00 -080030import java.nio.charset.StandardCharsets;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080031import java.nio.file.FileVisitResult;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080032import java.nio.file.Files;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080033import java.nio.file.Path;
34import java.nio.file.Paths;
35import java.nio.file.SimpleFileVisitor;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080036import java.nio.file.StandardCopyOption;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080037import java.nio.file.attribute.BasicFileAttributes;
tom53efab52014-10-07 17:43:48 -070038import java.util.ArrayList;
Madan Jampani27b69c62015-05-15 15:49:02 -070039import java.util.Arrays;
Brian O'Connore2eac102015-02-12 18:30:22 -080040import java.util.Collection;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070041import java.util.Dictionary;
tom53efab52014-10-07 17:43:48 -070042import java.util.List;
Sho SHIMIZUb5638b82016-02-11 14:55:05 -080043import java.util.Optional;
Thomas Vachuskaadba1522015-06-04 15:08:30 -070044import java.util.Random;
Ray Milkey36992c82015-11-17 13:31:15 -080045import java.util.Set;
Madan Jampani27b69c62015-05-15 15:49:02 -070046import java.util.concurrent.CompletableFuture;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070047import java.util.concurrent.ExecutionException;
48import java.util.concurrent.Future;
tom5f38b3a2014-08-27 23:50:54 -070049import java.util.concurrent.ThreadFactory;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070050import java.util.concurrent.TimeUnit;
51import java.util.concurrent.TimeoutException;
Madan Jampania29c6772015-08-17 13:17:07 -070052import java.util.function.Function;
53import java.util.function.Supplier;
Sho SHIMIZU85803e22016-01-13 21:53:43 -080054import java.util.stream.Collectors;
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -070055import java.util.stream.Stream;
56import java.util.stream.StreamSupport;
tom5f38b3a2014-08-27 23:50:54 -070057
Jian Li66f15262016-03-03 11:18:40 -080058import static java.nio.file.Files.delete;
59import static java.nio.file.Files.walkFileTree;
60import static org.onlab.util.GroupedThreadFactory.groupedThreadFactory;
61import static org.slf4j.LoggerFactory.getLogger;
Ray Milkey705d9bc2014-11-18 08:19:00 -080062
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080063/**
64 * Miscellaneous utility methods.
65 */
tom5f38b3a2014-08-27 23:50:54 -070066public abstract class Tools {
67
68 private Tools() {
69 }
70
Thomas Vachuska02aeb032015-01-06 22:36:30 -080071 private static final Logger log = getLogger(Tools.class);
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080072
Thomas Vachuskaadba1522015-06-04 15:08:30 -070073 private static Random random = new Random();
74
tom5f38b3a2014-08-27 23:50:54 -070075 /**
76 * Returns a thread factory that produces threads named according to the
77 * supplied name pattern.
78 *
79 * @param pattern name pattern
80 * @return thread factory
81 */
82 public static ThreadFactory namedThreads(String pattern) {
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080083 return new ThreadFactoryBuilder()
84 .setNameFormat(pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080085 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
86 .build();
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080087 }
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080088
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080089 /**
90 * Returns a thread factory that produces threads named according to the
91 * supplied name pattern and from the specified thread-group. The thread
92 * group name is expected to be specified in slash-delimited format, e.g.
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080093 * {@code onos/intent}. The thread names will be produced by converting
94 * the thread group name into dash-delimited format and pre-pended to the
95 * specified pattern.
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080096 *
97 * @param groupName group name in slash-delimited format to indicate hierarchy
98 * @param pattern name pattern
99 * @return thread factory
100 */
101 public static ThreadFactory groupedThreads(String groupName, String pattern) {
Jian Li03e9fb02016-03-01 17:13:54 -0800102 return groupedThreads(groupName, pattern, log);
103 }
104
105 /**
106 * Returns a thread factory that produces threads named according to the
107 * supplied name pattern and from the specified thread-group. The thread
108 * group name is expected to be specified in slash-delimited format, e.g.
109 * {@code onos/intent}. The thread names will be produced by converting
110 * the thread group name into dash-delimited format and pre-pended to the
111 * specified pattern. If a logger is specified, it will use the logger to
112 * print out the exception if it has any.
113 *
114 * @param groupName group name in slash-delimited format to indicate hierarchy
115 * @param pattern name pattern
116 * @param logger logger
117 * @return thread factory
118 */
119 public static ThreadFactory groupedThreads(String groupName, String pattern, Logger logger) {
120 if (logger == null) {
121 return groupedThreads(groupName, pattern);
122 }
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -0800123 return new ThreadFactoryBuilder()
124 .setThreadFactory(groupedThreadFactory(groupName))
Thomas Vachuskac13b90a2015-02-18 18:19:55 -0800125 .setNameFormat(groupName.replace(GroupedThreadFactory.DELIMITER, "-") + "-" + pattern)
Jian Li03e9fb02016-03-01 17:13:54 -0800126 .setUncaughtExceptionHandler((t, e) -> logger.error("Uncaught exception on " + t.getName(), e))
Thomas Vachuska480adad2015-03-06 10:27:09 -0800127 .build();
tom5f38b3a2014-08-27 23:50:54 -0700128 }
129
tom782a7cf2014-09-11 23:58:38 -0700130 /**
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800131 * Returns a thread factory that produces threads with MIN_PRIORITY.
132 *
133 * @param factory backing ThreadFactory
134 * @return thread factory
135 */
136 public static ThreadFactory minPriority(ThreadFactory factory) {
137 return new ThreadFactoryBuilder()
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800138 .setThreadFactory(factory)
139 .setPriority(Thread.MIN_PRIORITY)
140 .build();
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800141 }
142
143 /**
Brian O'Connore2eac102015-02-12 18:30:22 -0800144 * Returns true if the collection is null or is empty.
145 *
146 * @param collection collection to test
147 * @return true if null or empty; false otherwise
148 */
149 public static boolean isNullOrEmpty(Collection collection) {
150 return collection == null || collection.isEmpty();
151 }
152
153 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700154 * Returns the specified item if that item is not null; otherwise throws
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700155 * not found exception.
156 *
157 * @param item item to check
158 * @param message not found message
159 * @param <T> item type
160 * @return item if not null
161 * @throws org.onlab.util.ItemNotFoundException if item is null
162 */
163 public static <T> T nullIsNotFound(T item, String message) {
164 if (item == null) {
165 throw new ItemNotFoundException(message);
166 }
167 return item;
168 }
169
170 /**
Ray Milkey36992c82015-11-17 13:31:15 -0800171 * Returns the specified set if the set is not null and not empty;
172 * otherwise throws a not found exception.
173 *
174 * @param item set to check
175 * @param message not found message
176 * @param <T> Set item type
177 * @return item if not null and not empty
178 * @throws org.onlab.util.ItemNotFoundException if set is null or empty
179 */
180 public static <T> Set<T> emptyIsNotFound(Set<T> item, String message) {
181 if (item == null || item.isEmpty()) {
182 throw new ItemNotFoundException(message);
183 }
184 return item;
185 }
186
187 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700188 * Returns the specified item if that item is not null; otherwise throws
189 * bad argument exception.
190 *
191 * @param item item to check
192 * @param message not found message
193 * @param <T> item type
194 * @return item if not null
195 * @throws IllegalArgumentException if item is null
196 */
197 public static <T> T nullIsIllegal(T item, String message) {
198 if (item == null) {
199 throw new IllegalArgumentException(message);
200 }
201 return item;
202 }
203
204 /**
tom782a7cf2014-09-11 23:58:38 -0700205 * Converts a string from hex to long.
206 *
207 * @param string hex number in string form; sans 0x
208 * @return long value
209 */
210 public static long fromHex(String string) {
211 return UnsignedLongs.parseUnsignedLong(string, 16);
212 }
213
214 /**
215 * Converts a long value to hex string; 16 wide and sans 0x.
216 *
217 * @param value long value
218 * @return hex string
219 */
220 public static String toHex(long value) {
221 return Strings.padStart(UnsignedLongs.toString(value, 16), 16, '0');
222 }
223
224 /**
225 * Converts a long value to hex string; 16 wide and sans 0x.
226 *
227 * @param value long value
228 * @param width string width; zero padded
229 * @return hex string
230 */
231 public static String toHex(long value, int width) {
232 return Strings.padStart(UnsignedLongs.toString(value, 16), width, '0');
233 }
tomf110fff2014-09-26 00:38:18 -0700234
235 /**
Madan Jampanif2f086c2016-01-13 16:15:39 -0800236 * Returns the UTF-8 encoded byte[] representation of a String.
Jian Lidfba7392016-01-22 16:46:58 -0800237 * @param input input string
238 * @return UTF-8 encoded byte array
Madan Jampanif2f086c2016-01-13 16:15:39 -0800239 */
240 public static byte[] getBytesUtf8(String input) {
241 return input.getBytes(Charsets.UTF_8);
242 }
243
244 /**
245 * Returns the String representation of UTF-8 encoded byte[].
Jian Lidfba7392016-01-22 16:46:58 -0800246 * @param input input byte array
247 * @return UTF-8 encoded string
Madan Jampanif2f086c2016-01-13 16:15:39 -0800248 */
249 public static String toStringUtf8(byte[] input) {
250 return new String(input, Charsets.UTF_8);
251 }
252
253 /**
Madan Jampani9eb55d12015-08-14 07:47:56 -0700254 * Returns a copy of the input byte array.
255 *
256 * @param original input
257 * @return copy of original
258 */
259 public static byte[] copyOf(byte[] original) {
260 return Arrays.copyOf(original, original.length);
261 }
262
263 /**
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700264 * Get property as a string value.
265 *
266 * @param properties properties to be looked up
267 * @param propertyName the name of the property to look up
268 * @return value when the propertyName is defined or return null
269 */
270 public static String get(Dictionary<?, ?> properties, String propertyName) {
271 Object v = properties.get(propertyName);
272 String s = (v instanceof String) ? (String) v :
273 v != null ? v.toString() : null;
274 return Strings.isNullOrEmpty(s) ? null : s.trim();
275 }
276
277 /**
Jian Lid9b5f552016-03-11 18:15:31 -0800278 * Get Integer property from the propertyName
279 * Return null if propertyName is not found.
280 *
281 * @param properties properties to be looked up
282 * @param propertyName the name of the property to look up
283 * @return value when the propertyName is defined or return null
284 */
285 public static Integer getIntegerProperty(Dictionary<?, ?> properties,
286 String propertyName) {
287 Integer value;
288 try {
289 String s = get(properties, propertyName);
290 value = Strings.isNullOrEmpty(s) ? null : Integer.valueOf(s);
291 } catch (NumberFormatException | ClassCastException e) {
292 value = null;
293 }
294 return value;
295 }
296
297 /**
298 * Get Integer property from the propertyName
299 * Return default value if propertyName is not found.
300 *
301 * @param properties properties to be looked up
302 * @param propertyName the name of the property to look up
303 * @param defaultValue the default value that to be assigned
304 * @return value when the propertyName is defined or return default value
305 */
306 public static int getIntegerProperty(Dictionary<?, ?> properties,
307 String propertyName,
308 int defaultValue) {
309 try {
310 String s = get(properties, propertyName);
311 return Strings.isNullOrEmpty(s) ? defaultValue : Integer.valueOf(s);
312 } catch (NumberFormatException | ClassCastException e) {
313 return defaultValue;
314 }
315 }
316
317 /**
318 * Check property name is defined and set to true.
319 *
320 * @param properties properties to be looked up
321 * @param propertyName the name of the property to look up
322 * @return value when the propertyName is defined or return null
323 */
324 public static Boolean isPropertyEnabled(Dictionary<?, ?> properties,
325 String propertyName) {
326 Boolean value;
327 try {
328 String s = get(properties, propertyName);
329 value = Strings.isNullOrEmpty(s) ? null : Boolean.valueOf(s);
330 } catch (ClassCastException e) {
331 value = null;
332 }
333 return value;
334 }
335
336 /**
337 * Check property name is defined as set to true.
338 *
339 * @param properties properties to be looked up
340 * @param propertyName the name of the property to look up
341 * @param defaultValue the default value that to be assigned
342 * @return value when the propertyName is defined or return the default value
343 */
344 public static boolean isPropertyEnabled(Dictionary<?, ?> properties,
345 String propertyName,
346 boolean defaultValue) {
347 try {
348 String s = get(properties, propertyName);
349 return Strings.isNullOrEmpty(s) ? defaultValue : Boolean.valueOf(s);
350 } catch (ClassCastException e) {
351 return defaultValue;
352 }
353 }
354
355 /**
tomf110fff2014-09-26 00:38:18 -0700356 * Suspends the current thread for a specified number of millis.
357 *
358 * @param ms number of millis
359 */
360 public static void delay(int ms) {
361 try {
362 Thread.sleep(ms);
363 } catch (InterruptedException e) {
364 throw new RuntimeException("Interrupted", e);
365 }
366 }
367
tom53efab52014-10-07 17:43:48 -0700368 /**
Madan Jampania29c6772015-08-17 13:17:07 -0700369 * Returns a function that retries execution on failure.
370 * @param base base function
371 * @param exceptionClass type of exception for which to retry
372 * @param maxRetries max number of retries before giving up
373 * @param maxDelayBetweenRetries max delay between successive retries. The actual delay is randomly picked from
374 * the interval (0, maxDelayBetweenRetries]
375 * @return function
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700376 * @param <U> type of function input
377 * @param <V> type of function output
Madan Jampania29c6772015-08-17 13:17:07 -0700378 */
379 public static <U, V> Function<U, V> retryable(Function<U, V> base,
380 Class<? extends Throwable> exceptionClass,
381 int maxRetries,
382 int maxDelayBetweenRetries) {
383 return new RetryingFunction<>(base, exceptionClass, maxRetries, maxDelayBetweenRetries);
384 }
385
386 /**
387 * Returns a Supplier that retries execution on failure.
388 * @param base base supplier
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 supplier
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700394 * @param <V> type of supplied result
Madan Jampania29c6772015-08-17 13:17:07 -0700395 */
396 public static <V> Supplier<V> retryable(Supplier<V> base,
397 Class<? extends Throwable> exceptionClass,
398 int maxRetries,
399 int maxDelayBetweenRetries) {
400 return () -> new RetryingFunction<>(v -> base.get(),
401 exceptionClass,
402 maxRetries,
403 maxDelayBetweenRetries).apply(null);
404 }
405
406 /**
Thomas Vachuskaadba1522015-06-04 15:08:30 -0700407 * Suspends the current thread for a random number of millis between 0 and
408 * the indicated limit.
409 *
410 * @param ms max number of millis
411 */
412 public static void randomDelay(int ms) {
413 try {
414 Thread.sleep(random.nextInt(ms));
415 } catch (InterruptedException e) {
416 throw new RuntimeException("Interrupted", e);
417 }
418 }
419
420 /**
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700421 * Suspends the current thread for a specified number of millis and nanos.
422 *
423 * @param ms number of millis
424 * @param nanos number of nanos
425 */
426 public static void delay(int ms, int nanos) {
427 try {
428 Thread.sleep(ms, nanos);
429 } catch (InterruptedException e) {
430 throw new RuntimeException("Interrupted", e);
431 }
432 }
433
434 /**
tom53efab52014-10-07 17:43:48 -0700435 * Slurps the contents of a file into a list of strings, one per line.
436 *
437 * @param path file path
438 * @return file contents
HIGUCHI Yuta3b3bd1e2015-09-22 16:39:33 -0700439 * @deprecated in Emu release
tom53efab52014-10-07 17:43:48 -0700440 */
HIGUCHI Yuta3b3bd1e2015-09-22 16:39:33 -0700441 @Deprecated
tom53efab52014-10-07 17:43:48 -0700442 public static List<String> slurp(File path) {
HIGUCHI Yutacf4d6172015-09-22 16:39:33 -0700443 try (
Ray Milkey705d9bc2014-11-18 08:19:00 -0800444 BufferedReader br = new BufferedReader(
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800445 new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8));
HIGUCHI Yutacf4d6172015-09-22 16:39:33 -0700446 ) {
tom53efab52014-10-07 17:43:48 -0700447 List<String> lines = new ArrayList<>();
448 String line;
449 while ((line = br.readLine()) != null) {
450 lines.add(line);
451 }
452 return lines;
453
454 } catch (IOException e) {
455 return null;
456 }
457 }
458
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800459 /**
460 * Purges the specified directory path.&nbsp;Use with great caution since
461 * no attempt is made to check for symbolic links, which could result in
462 * deletion of unintended files.
463 *
464 * @param path directory to be removed
465 * @throws java.io.IOException if unable to remove contents
466 */
467 public static void removeDirectory(String path) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800468 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700469 File dir = new File(path);
470 if (dir.exists() && dir.isDirectory()) {
471 walkFileTree(Paths.get(path), visitor);
472 if (visitor.exception != null) {
473 throw visitor.exception;
474 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800475 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800476 }
477
478 /**
479 * Purges the specified directory path.&nbsp;Use with great caution since
480 * no attempt is made to check for symbolic links, which could result in
481 * deletion of unintended files.
482 *
483 * @param dir directory to be removed
484 * @throws java.io.IOException if unable to remove contents
485 */
486 public static void removeDirectory(File dir) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800487 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700488 if (dir.exists() && dir.isDirectory()) {
489 walkFileTree(Paths.get(dir.getAbsolutePath()), visitor);
490 if (visitor.exception != null) {
491 throw visitor.exception;
492 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800493 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800494 }
495
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800496 // Auxiliary path visitor for recursive directory structure removal.
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800497 private static class DirectoryDeleter extends SimpleFileVisitor<Path> {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800498
499 private IOException exception;
500
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800501 @Override
502 public FileVisitResult visitFile(Path file, BasicFileAttributes attributes)
503 throws IOException {
504 if (attributes.isRegularFile()) {
505 delete(file);
506 }
507 return FileVisitResult.CONTINUE;
508 }
509
510 @Override
511 public FileVisitResult postVisitDirectory(Path directory, IOException ioe)
512 throws IOException {
513 delete(directory);
514 return FileVisitResult.CONTINUE;
515 }
516
517 @Override
518 public FileVisitResult visitFileFailed(Path file, IOException ioe)
519 throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800520 this.exception = ioe;
521 return FileVisitResult.TERMINATE;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800522 }
523 }
524
Madan Jampani30a57f82015-03-02 12:19:41 -0800525 /**
526 * Returns a human friendly time ago string for a specified system time.
Thomas Vachuska480adad2015-03-06 10:27:09 -0800527 *
Madan Jampani30a57f82015-03-02 12:19:41 -0800528 * @param unixTime system time in millis
529 * @return human friendly time ago
530 */
531 public static String timeAgo(long unixTime) {
532 long deltaMillis = System.currentTimeMillis() - unixTime;
533 long secondsSince = (long) (deltaMillis / 1000.0);
534 long minsSince = (long) (deltaMillis / (1000.0 * 60));
535 long hoursSince = (long) (deltaMillis / (1000.0 * 60 * 60));
536 long daysSince = (long) (deltaMillis / (1000.0 * 60 * 60 * 24));
537 if (daysSince > 0) {
538 return String.format("%dd ago", daysSince);
539 } else if (hoursSince > 0) {
540 return String.format("%dh ago", hoursSince);
541 } else if (minsSince > 0) {
542 return String.format("%dm ago", minsSince);
543 } else if (secondsSince > 0) {
544 return String.format("%ds ago", secondsSince);
545 } else {
546 return "just now";
547 }
548 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800549
550 /**
551 * Copies the specified directory path.&nbsp;Use with great caution since
552 * no attempt is made to check for symbolic links, which could result in
553 * copy of unintended files.
554 *
555 * @param src directory to be copied
556 * @param dst destination directory to be removed
557 * @throws java.io.IOException if unable to remove contents
558 */
559 public static void copyDirectory(String src, String dst) throws IOException {
560 walkFileTree(Paths.get(src), new DirectoryCopier(src, dst));
561 }
562
563 /**
564 * Copies the specified directory path.&nbsp;Use with great caution since
565 * no attempt is made to check for symbolic links, which could result in
566 * copy of unintended files.
567 *
568 * @param src directory to be copied
569 * @param dst destination directory to be removed
570 * @throws java.io.IOException if unable to remove contents
571 */
572 public static void copyDirectory(File src, File dst) throws IOException {
573 walkFileTree(Paths.get(src.getAbsolutePath()),
574 new DirectoryCopier(src.getAbsolutePath(),
575 dst.getAbsolutePath()));
576 }
577
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700578 /**
579 * Returns the future value when complete or if future
580 * completes exceptionally returns the defaultValue.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700581 *
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700582 * @param future future
583 * @param defaultValue default value
584 * @param <T> future value type
585 * @return future value when complete or if future
586 * completes exceptionally returns the defaultValue.
587 */
588 public static <T> T futureGetOrElse(Future<T> future, T defaultValue) {
589 try {
590 return future.get();
591 } catch (InterruptedException e) {
592 Thread.currentThread().interrupt();
593 return defaultValue;
594 } catch (ExecutionException e) {
595 return defaultValue;
596 }
597 }
598
599 /**
600 * Returns the future value when complete or if future
601 * completes exceptionally returns the defaultValue.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700602 *
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700603 * @param future future
604 * @param timeout time to wait for successful completion
605 * @param timeUnit time unit
606 * @param defaultValue default value
607 * @param <T> future value type
608 * @return future value when complete or if future
609 * completes exceptionally returns the defaultValue.
610 */
611 public static <T> T futureGetOrElse(Future<T> future,
612 long timeout,
613 TimeUnit timeUnit,
614 T defaultValue) {
615 try {
616 return future.get(timeout, timeUnit);
617 } catch (InterruptedException e) {
618 Thread.currentThread().interrupt();
619 return defaultValue;
620 } catch (ExecutionException | TimeoutException e) {
621 return defaultValue;
622 }
623 }
624
Madan Jampani27b69c62015-05-15 15:49:02 -0700625 /**
626 * Returns a future that is completed exceptionally.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700627 *
Madan Jampani27b69c62015-05-15 15:49:02 -0700628 * @param t exception
629 * @param <T> future value type
630 * @return future
631 */
632 public static <T> CompletableFuture<T> exceptionalFuture(Throwable t) {
633 CompletableFuture<T> future = new CompletableFuture<>();
634 future.completeExceptionally(t);
635 return future;
636 }
637
638 /**
Sho SHIMIZU85803e22016-01-13 21:53:43 -0800639 * Returns a new CompletableFuture completed with a list of computed values
640 * when all of the given CompletableFuture complete.
641 *
642 * @param futures the CompletableFutures
643 * @param <T> value type of CompletableFuture
644 * @return a new CompletableFuture that is completed when all of the given CompletableFutures complete
645 */
646 public static <T> CompletableFuture<List<T>> allOf(List<CompletableFuture<T>> futures) {
647 return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]))
648 .thenApply(v -> futures.stream()
649 .map(CompletableFuture::join)
650 .collect(Collectors.toList())
651 );
652 }
653
654 /**
Madan Jampani27b69c62015-05-15 15:49:02 -0700655 * Returns the contents of {@code ByteBuffer} as byte array.
656 * <p>
657 * WARNING: There is a performance cost due to array copy
658 * when using this method.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700659 *
Madan Jampani27b69c62015-05-15 15:49:02 -0700660 * @param buffer byte buffer
661 * @return byte array containing the byte buffer contents
662 */
663 public static byte[] byteBuffertoArray(ByteBuffer buffer) {
664 int length = buffer.remaining();
665 if (buffer.hasArray()) {
666 int offset = buffer.arrayOffset() + buffer.position();
667 return Arrays.copyOfRange(buffer.array(), offset, offset + length);
668 }
669 byte[] bytes = new byte[length];
670 buffer.duplicate().get(bytes);
671 return bytes;
672 }
673
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700674 /**
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700675 * Converts an iterable to a stream.
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700676 *
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700677 * @param it iterable to convert
678 * @param <T> type if item
679 * @return iterable as a stream
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700680 */
681 public static <T> Stream<T> stream(Iterable<T> it) {
682 return StreamSupport.stream(it.spliterator(), false);
683 }
684
Sho SHIMIZUb5638b82016-02-11 14:55:05 -0800685 /**
686 * Converts an optional to a stream.
687 *
688 * @param optional optional to convert
689 * @param <T> type of enclosed value
690 * @return optional as a stream
691 */
692 public static <T> Stream<T> stream(Optional<T> optional) {
693 return optional.map(Stream::of).orElse(Stream.empty());
694 }
695
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800696 // Auxiliary path visitor for recursive directory structure copying.
697 private static class DirectoryCopier extends SimpleFileVisitor<Path> {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800698 private Path src;
699 private Path dst;
700 private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING;
701
702 DirectoryCopier(String src, String dst) {
703 this.src = Paths.get(src);
704 this.dst = Paths.get(dst);
705 }
706
707 @Override
708 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
709 Path targetPath = dst.resolve(src.relativize(dir));
710 if (!Files.exists(targetPath)) {
711 Files.createDirectory(targetPath);
712 }
713 return FileVisitResult.CONTINUE;
714 }
715
716 @Override
717 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
718 Files.copy(file, dst.resolve(src.relativize(file)), copyOption);
719 return FileVisitResult.CONTINUE;
720 }
721 }
722
tom5f38b3a2014-08-27 23:50:54 -0700723}