blob: 69baf47a4a6d6eb3097c3b1d18b5fcc7b3a78e47 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
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
Jonathan Hartcc962d82016-08-09 16:52:22 -070018import com.google.common.base.Charsets;
19import com.google.common.base.Strings;
20import com.google.common.collect.Lists;
21import com.google.common.primitives.UnsignedLongs;
22import com.google.common.util.concurrent.ThreadFactoryBuilder;
23import org.slf4j.Logger;
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080024
tom53efab52014-10-07 17:43:48 -070025import java.io.File;
tom53efab52014-10-07 17:43:48 -070026import java.io.IOException;
Madan Jampani27b69c62015-05-15 15:49:02 -070027import java.nio.ByteBuffer;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080028import java.nio.file.FileVisitResult;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080029import java.nio.file.Files;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080030import java.nio.file.Path;
31import java.nio.file.Paths;
32import java.nio.file.SimpleFileVisitor;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080033import java.nio.file.StandardCopyOption;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080034import java.nio.file.attribute.BasicFileAttributes;
Ray Milkeyb68bbbc2017-12-18 10:05:49 -080035import java.security.SecureRandom;
Yuta HIGUCHI0c47d532017-08-18 23:16:35 -070036import java.time.Instant;
37import java.time.OffsetDateTime;
38import java.time.ZoneId;
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;
Jordan Halterman9bdc24f2017-04-19 23:45:12 -070048import java.util.concurrent.Executor;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070049import java.util.concurrent.Future;
tom5f38b3a2014-08-27 23:50:54 -070050import java.util.concurrent.ThreadFactory;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070051import java.util.concurrent.TimeUnit;
52import java.util.concurrent.TimeoutException;
Madan Jampani307a21e2016-09-01 15:49:47 -070053import java.util.function.BinaryOperator;
Madan Jampania29c6772015-08-17 13:17:07 -070054import java.util.function.Function;
55import java.util.function.Supplier;
Sho SHIMIZU85803e22016-01-13 21:53:43 -080056import java.util.stream.Collectors;
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -070057import java.util.stream.Stream;
58import java.util.stream.StreamSupport;
tom5f38b3a2014-08-27 23:50:54 -070059
Yuta HIGUCHI47d96092017-11-17 14:05:26 -080060import static com.google.common.base.Preconditions.checkNotNull;
Jonathan Hartcc962d82016-08-09 16:52:22 -070061import static java.nio.file.Files.delete;
62import static java.nio.file.Files.walkFileTree;
63import static org.onlab.util.GroupedThreadFactory.groupedThreadFactory;
64import static org.slf4j.LoggerFactory.getLogger;
Ray Milkey705d9bc2014-11-18 08:19:00 -080065
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080066/**
67 * Miscellaneous utility methods.
68 */
tom5f38b3a2014-08-27 23:50:54 -070069public abstract class Tools {
70
71 private Tools() {
72 }
73
Thomas Vachuska02aeb032015-01-06 22:36:30 -080074 private static final Logger log = getLogger(Tools.class);
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080075
Ray Milkeyb68bbbc2017-12-18 10:05:49 -080076 private static Random random = new SecureRandom();
Thomas Vachuskaadba1522015-06-04 15:08:30 -070077
tom5f38b3a2014-08-27 23:50:54 -070078 /**
79 * Returns a thread factory that produces threads named according to the
80 * supplied name pattern.
81 *
82 * @param pattern name pattern
83 * @return thread factory
84 */
85 public static ThreadFactory namedThreads(String pattern) {
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080086 return new ThreadFactoryBuilder()
87 .setNameFormat(pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080088 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
89 .build();
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080090 }
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080091
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080092 /**
93 * Returns a thread factory that produces threads named according to the
94 * supplied name pattern and from the specified thread-group. The thread
95 * group name is expected to be specified in slash-delimited format, e.g.
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080096 * {@code onos/intent}. The thread names will be produced by converting
97 * the thread group name into dash-delimited format and pre-pended to the
98 * specified pattern.
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080099 *
100 * @param groupName group name in slash-delimited format to indicate hierarchy
101 * @param pattern name pattern
102 * @return thread factory
103 */
104 public static ThreadFactory groupedThreads(String groupName, String pattern) {
Jian Li03e9fb02016-03-01 17:13:54 -0800105 return groupedThreads(groupName, pattern, log);
106 }
107
108 /**
109 * Returns a thread factory that produces threads named according to the
110 * supplied name pattern and from the specified thread-group. The thread
111 * group name is expected to be specified in slash-delimited format, e.g.
112 * {@code onos/intent}. The thread names will be produced by converting
113 * the thread group name into dash-delimited format and pre-pended to the
114 * specified pattern. If a logger is specified, it will use the logger to
115 * print out the exception if it has any.
116 *
117 * @param groupName group name in slash-delimited format to indicate hierarchy
118 * @param pattern name pattern
119 * @param logger logger
120 * @return thread factory
121 */
122 public static ThreadFactory groupedThreads(String groupName, String pattern, Logger logger) {
123 if (logger == null) {
124 return groupedThreads(groupName, pattern);
125 }
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -0800126 return new ThreadFactoryBuilder()
127 .setThreadFactory(groupedThreadFactory(groupName))
Thomas Vachuskac13b90a2015-02-18 18:19:55 -0800128 .setNameFormat(groupName.replace(GroupedThreadFactory.DELIMITER, "-") + "-" + pattern)
Jian Li03e9fb02016-03-01 17:13:54 -0800129 .setUncaughtExceptionHandler((t, e) -> logger.error("Uncaught exception on " + t.getName(), e))
Thomas Vachuska480adad2015-03-06 10:27:09 -0800130 .build();
tom5f38b3a2014-08-27 23:50:54 -0700131 }
132
tom782a7cf2014-09-11 23:58:38 -0700133 /**
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800134 * Returns a thread factory that produces threads with MIN_PRIORITY.
135 *
136 * @param factory backing ThreadFactory
137 * @return thread factory
138 */
139 public static ThreadFactory minPriority(ThreadFactory factory) {
140 return new ThreadFactoryBuilder()
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800141 .setThreadFactory(factory)
142 .setPriority(Thread.MIN_PRIORITY)
143 .build();
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800144 }
145
146 /**
Yuta HIGUCHIa2a11cd2016-12-19 14:19:11 -0800147 * Returns a thread factory that produces threads with MAX_PRIORITY.
148 *
149 * @param factory backing ThreadFactory
150 * @return thread factory
151 */
152 public static ThreadFactory maxPriority(ThreadFactory factory) {
153 return new ThreadFactoryBuilder()
154 .setThreadFactory(factory)
155 .setPriority(Thread.MAX_PRIORITY)
156 .build();
157 }
158
159 /**
Brian O'Connore2eac102015-02-12 18:30:22 -0800160 * Returns true if the collection is null or is empty.
161 *
162 * @param collection collection to test
163 * @return true if null or empty; false otherwise
164 */
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800165 public static boolean isNullOrEmpty(Collection<?> collection) {
Brian O'Connore2eac102015-02-12 18:30:22 -0800166 return collection == null || collection.isEmpty();
167 }
168
169 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700170 * Returns the specified item if that item is not null; otherwise throws
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700171 * not found exception.
172 *
173 * @param item item to check
174 * @param message not found message
175 * @param <T> item type
176 * @return item if not null
177 * @throws org.onlab.util.ItemNotFoundException if item is null
178 */
179 public static <T> T nullIsNotFound(T item, String message) {
180 if (item == null) {
181 throw new ItemNotFoundException(message);
182 }
183 return item;
184 }
185
186 /**
Ray Milkey36992c82015-11-17 13:31:15 -0800187 * Returns the specified set if the set is not null and not empty;
188 * otherwise throws a not found exception.
189 *
190 * @param item set to check
191 * @param message not found message
192 * @param <T> Set item type
193 * @return item if not null and not empty
194 * @throws org.onlab.util.ItemNotFoundException if set is null or empty
195 */
196 public static <T> Set<T> emptyIsNotFound(Set<T> item, String message) {
197 if (item == null || item.isEmpty()) {
198 throw new ItemNotFoundException(message);
199 }
200 return item;
201 }
202
203 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700204 * Returns the specified item if that item is not null; otherwise throws
205 * bad argument exception.
206 *
207 * @param item item to check
208 * @param message not found message
209 * @param <T> item type
210 * @return item if not null
211 * @throws IllegalArgumentException if item is null
212 */
213 public static <T> T nullIsIllegal(T item, String message) {
214 if (item == null) {
215 throw new IllegalArgumentException(message);
216 }
217 return item;
218 }
219
220 /**
tom782a7cf2014-09-11 23:58:38 -0700221 * Converts a string from hex to long.
222 *
223 * @param string hex number in string form; sans 0x
224 * @return long value
225 */
226 public static long fromHex(String string) {
227 return UnsignedLongs.parseUnsignedLong(string, 16);
228 }
229
230 /**
231 * Converts a long value to hex string; 16 wide and sans 0x.
232 *
233 * @param value long value
234 * @return hex string
235 */
236 public static String toHex(long value) {
237 return Strings.padStart(UnsignedLongs.toString(value, 16), 16, '0');
238 }
239
240 /**
241 * Converts a long value to hex string; 16 wide and sans 0x.
242 *
243 * @param value long value
244 * @param width string width; zero padded
245 * @return hex string
246 */
247 public static String toHex(long value, int width) {
248 return Strings.padStart(UnsignedLongs.toString(value, 16), width, '0');
249 }
tomf110fff2014-09-26 00:38:18 -0700250
251 /**
Jonathan Hartcc962d82016-08-09 16:52:22 -0700252 * Returns a string encoding in hex of the given long value with prefix
253 * '0x'.
254 *
255 * @param value long value to encode as hex string
256 * @return hex string
257 */
258 public static String toHexWithPrefix(long value) {
259 return "0x" + Long.toHexString(value);
260 }
261
262 /**
Madan Jampanif2f086c2016-01-13 16:15:39 -0800263 * Returns the UTF-8 encoded byte[] representation of a String.
Jian Lidfba7392016-01-22 16:46:58 -0800264 * @param input input string
265 * @return UTF-8 encoded byte array
Madan Jampanif2f086c2016-01-13 16:15:39 -0800266 */
267 public static byte[] getBytesUtf8(String input) {
268 return input.getBytes(Charsets.UTF_8);
269 }
270
271 /**
272 * Returns the String representation of UTF-8 encoded byte[].
Jian Lidfba7392016-01-22 16:46:58 -0800273 * @param input input byte array
274 * @return UTF-8 encoded string
Madan Jampanif2f086c2016-01-13 16:15:39 -0800275 */
276 public static String toStringUtf8(byte[] input) {
277 return new String(input, Charsets.UTF_8);
278 }
279
280 /**
Madan Jampani9eb55d12015-08-14 07:47:56 -0700281 * Returns a copy of the input byte array.
282 *
283 * @param original input
284 * @return copy of original
285 */
286 public static byte[] copyOf(byte[] original) {
287 return Arrays.copyOf(original, original.length);
288 }
289
290 /**
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700291 * Get property as a string value.
292 *
293 * @param properties properties to be looked up
294 * @param propertyName the name of the property to look up
295 * @return value when the propertyName is defined or return null
296 */
297 public static String get(Dictionary<?, ?> properties, String propertyName) {
298 Object v = properties.get(propertyName);
299 String s = (v instanceof String) ? (String) v :
300 v != null ? v.toString() : null;
301 return Strings.isNullOrEmpty(s) ? null : s.trim();
302 }
303
304 /**
Jian Lid9b5f552016-03-11 18:15:31 -0800305 * Get Integer property from the propertyName
306 * Return null if propertyName is not found.
307 *
308 * @param properties properties to be looked up
309 * @param propertyName the name of the property to look up
310 * @return value when the propertyName is defined or return null
311 */
312 public static Integer getIntegerProperty(Dictionary<?, ?> properties,
313 String propertyName) {
314 Integer value;
315 try {
316 String s = get(properties, propertyName);
317 value = Strings.isNullOrEmpty(s) ? null : Integer.valueOf(s);
318 } catch (NumberFormatException | ClassCastException e) {
319 value = null;
320 }
321 return value;
322 }
323
324 /**
325 * Get Integer property from the propertyName
326 * Return default value if propertyName is not found.
327 *
328 * @param properties properties to be looked up
329 * @param propertyName the name of the property to look up
330 * @param defaultValue the default value that to be assigned
331 * @return value when the propertyName is defined or return default value
332 */
333 public static int getIntegerProperty(Dictionary<?, ?> properties,
334 String propertyName,
335 int defaultValue) {
336 try {
337 String s = get(properties, propertyName);
338 return Strings.isNullOrEmpty(s) ? defaultValue : Integer.valueOf(s);
339 } catch (NumberFormatException | ClassCastException e) {
340 return defaultValue;
341 }
342 }
343
344 /**
345 * Check property name is defined and set to true.
346 *
347 * @param properties properties to be looked up
348 * @param propertyName the name of the property to look up
349 * @return value when the propertyName is defined or return null
350 */
351 public static Boolean isPropertyEnabled(Dictionary<?, ?> properties,
352 String propertyName) {
353 Boolean value;
354 try {
355 String s = get(properties, propertyName);
356 value = Strings.isNullOrEmpty(s) ? null : Boolean.valueOf(s);
357 } catch (ClassCastException e) {
358 value = null;
359 }
360 return value;
361 }
362
363 /**
364 * Check property name is defined as set to true.
365 *
366 * @param properties properties to be looked up
367 * @param propertyName the name of the property to look up
368 * @param defaultValue the default value that to be assigned
369 * @return value when the propertyName is defined or return the default value
370 */
371 public static boolean isPropertyEnabled(Dictionary<?, ?> properties,
372 String propertyName,
373 boolean defaultValue) {
374 try {
375 String s = get(properties, propertyName);
376 return Strings.isNullOrEmpty(s) ? defaultValue : Boolean.valueOf(s);
377 } catch (ClassCastException e) {
378 return defaultValue;
379 }
380 }
381
382 /**
tomf110fff2014-09-26 00:38:18 -0700383 * Suspends the current thread for a specified number of millis.
384 *
385 * @param ms number of millis
386 */
387 public static void delay(int ms) {
388 try {
389 Thread.sleep(ms);
390 } catch (InterruptedException e) {
Ray Milkey5c7d4882018-02-05 14:50:39 -0800391 Thread.currentThread().interrupt();
Ray Milkey986a47a2018-01-25 11:38:51 -0800392 throw new IllegalStateException("Interrupted", e);
tomf110fff2014-09-26 00:38:18 -0700393 }
394 }
395
tom53efab52014-10-07 17:43:48 -0700396 /**
sdn94b00152016-08-30 02:12:32 -0700397 * Get Long property from the propertyName
398 * Return null if propertyName is not found.
399 *
400 * @param properties properties to be looked up
401 * @param propertyName the name of the property to look up
402 * @return value when the propertyName is defined or return null
403 */
404 public static Long getLongProperty(Dictionary<?, ?> properties,
405 String propertyName) {
406 Long value;
407 try {
408 String s = get(properties, propertyName);
409 value = Strings.isNullOrEmpty(s) ? null : Long.valueOf(s);
410 } catch (NumberFormatException | ClassCastException e) {
411 value = null;
412 }
413 return value;
414 }
415
416 /**
Madan Jampania29c6772015-08-17 13:17:07 -0700417 * Returns a function that retries execution on failure.
418 * @param base base function
419 * @param exceptionClass type of exception for which to retry
420 * @param maxRetries max number of retries before giving up
421 * @param maxDelayBetweenRetries max delay between successive retries. The actual delay is randomly picked from
422 * the interval (0, maxDelayBetweenRetries]
423 * @return function
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700424 * @param <U> type of function input
425 * @param <V> type of function output
Madan Jampania29c6772015-08-17 13:17:07 -0700426 */
427 public static <U, V> Function<U, V> retryable(Function<U, V> base,
428 Class<? extends Throwable> exceptionClass,
429 int maxRetries,
430 int maxDelayBetweenRetries) {
431 return new RetryingFunction<>(base, exceptionClass, maxRetries, maxDelayBetweenRetries);
432 }
433
434 /**
435 * Returns a Supplier that retries execution on failure.
436 * @param base base supplier
437 * @param exceptionClass type of exception for which to retry
438 * @param maxRetries max number of retries before giving up
439 * @param maxDelayBetweenRetries max delay between successive retries. The actual delay is randomly picked from
440 * the interval (0, maxDelayBetweenRetries]
441 * @return supplier
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700442 * @param <V> type of supplied result
Madan Jampania29c6772015-08-17 13:17:07 -0700443 */
444 public static <V> Supplier<V> retryable(Supplier<V> base,
445 Class<? extends Throwable> exceptionClass,
446 int maxRetries,
447 int maxDelayBetweenRetries) {
448 return () -> new RetryingFunction<>(v -> base.get(),
449 exceptionClass,
450 maxRetries,
451 maxDelayBetweenRetries).apply(null);
452 }
453
454 /**
Thomas Vachuskaadba1522015-06-04 15:08:30 -0700455 * Suspends the current thread for a random number of millis between 0 and
456 * the indicated limit.
457 *
458 * @param ms max number of millis
459 */
460 public static void randomDelay(int ms) {
461 try {
462 Thread.sleep(random.nextInt(ms));
463 } catch (InterruptedException e) {
Ray Milkey5c7d4882018-02-05 14:50:39 -0800464 Thread.currentThread().interrupt();
Ray Milkey986a47a2018-01-25 11:38:51 -0800465 throw new IllegalStateException("Interrupted", e);
Thomas Vachuskaadba1522015-06-04 15:08:30 -0700466 }
467 }
468
469 /**
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700470 * Suspends the current thread for a specified number of millis and nanos.
471 *
472 * @param ms number of millis
473 * @param nanos number of nanos
474 */
475 public static void delay(int ms, int nanos) {
476 try {
477 Thread.sleep(ms, nanos);
478 } catch (InterruptedException e) {
Ray Milkey5c7d4882018-02-05 14:50:39 -0800479 Thread.currentThread().interrupt();
Ray Milkey986a47a2018-01-25 11:38:51 -0800480 throw new IllegalStateException("Interrupted", e);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700481 }
482 }
483
484 /**
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800485 * Purges the specified directory path.&nbsp;Use with great caution since
486 * no attempt is made to check for symbolic links, which could result in
487 * deletion of unintended files.
488 *
489 * @param path directory to be removed
490 * @throws java.io.IOException if unable to remove contents
491 */
492 public static void removeDirectory(String path) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800493 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700494 File dir = new File(path);
495 if (dir.exists() && dir.isDirectory()) {
496 walkFileTree(Paths.get(path), visitor);
497 if (visitor.exception != null) {
498 throw visitor.exception;
499 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800500 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800501 }
502
503 /**
504 * Purges the specified directory path.&nbsp;Use with great caution since
505 * no attempt is made to check for symbolic links, which could result in
506 * deletion of unintended files.
507 *
508 * @param dir directory to be removed
509 * @throws java.io.IOException if unable to remove contents
510 */
511 public static void removeDirectory(File dir) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800512 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700513 if (dir.exists() && dir.isDirectory()) {
514 walkFileTree(Paths.get(dir.getAbsolutePath()), visitor);
515 if (visitor.exception != null) {
516 throw visitor.exception;
517 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800518 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800519 }
520
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800521 // Auxiliary path visitor for recursive directory structure removal.
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800522 private static class DirectoryDeleter extends SimpleFileVisitor<Path> {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800523
524 private IOException exception;
525
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800526 @Override
527 public FileVisitResult visitFile(Path file, BasicFileAttributes attributes)
528 throws IOException {
529 if (attributes.isRegularFile()) {
530 delete(file);
531 }
532 return FileVisitResult.CONTINUE;
533 }
534
535 @Override
536 public FileVisitResult postVisitDirectory(Path directory, IOException ioe)
537 throws IOException {
538 delete(directory);
539 return FileVisitResult.CONTINUE;
540 }
541
542 @Override
543 public FileVisitResult visitFileFailed(Path file, IOException ioe)
544 throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800545 this.exception = ioe;
546 return FileVisitResult.TERMINATE;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800547 }
548 }
549
Madan Jampani30a57f82015-03-02 12:19:41 -0800550 /**
551 * Returns a human friendly time ago string for a specified system time.
Thomas Vachuska480adad2015-03-06 10:27:09 -0800552 *
Madan Jampani30a57f82015-03-02 12:19:41 -0800553 * @param unixTime system time in millis
554 * @return human friendly time ago
555 */
556 public static String timeAgo(long unixTime) {
557 long deltaMillis = System.currentTimeMillis() - unixTime;
558 long secondsSince = (long) (deltaMillis / 1000.0);
559 long minsSince = (long) (deltaMillis / (1000.0 * 60));
560 long hoursSince = (long) (deltaMillis / (1000.0 * 60 * 60));
561 long daysSince = (long) (deltaMillis / (1000.0 * 60 * 60 * 24));
562 if (daysSince > 0) {
Saurav Dasd5ec9e92017-01-17 10:40:18 -0800563 return String.format("%dd%dh ago", daysSince, hoursSince - daysSince * 24);
Madan Jampani30a57f82015-03-02 12:19:41 -0800564 } else if (hoursSince > 0) {
Saurav Dasd5ec9e92017-01-17 10:40:18 -0800565 return String.format("%dh%dm ago", hoursSince, minsSince - hoursSince * 60);
Madan Jampani30a57f82015-03-02 12:19:41 -0800566 } else if (minsSince > 0) {
Saurav Dasd5ec9e92017-01-17 10:40:18 -0800567 return String.format("%dm%ds ago", minsSince, secondsSince - minsSince * 60);
Madan Jampani30a57f82015-03-02 12:19:41 -0800568 } else if (secondsSince > 0) {
569 return String.format("%ds ago", secondsSince);
570 } else {
571 return "just now";
572 }
573 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800574
575 /**
576 * Copies the specified directory path.&nbsp;Use with great caution since
577 * no attempt is made to check for symbolic links, which could result in
578 * copy of unintended files.
579 *
580 * @param src directory to be copied
581 * @param dst destination directory to be removed
582 * @throws java.io.IOException if unable to remove contents
583 */
584 public static void copyDirectory(String src, String dst) throws IOException {
585 walkFileTree(Paths.get(src), new DirectoryCopier(src, dst));
586 }
587
588 /**
589 * Copies the specified directory path.&nbsp;Use with great caution since
590 * no attempt is made to check for symbolic links, which could result in
591 * copy of unintended files.
592 *
593 * @param src directory to be copied
594 * @param dst destination directory to be removed
595 * @throws java.io.IOException if unable to remove contents
596 */
597 public static void copyDirectory(File src, File dst) throws IOException {
598 walkFileTree(Paths.get(src.getAbsolutePath()),
599 new DirectoryCopier(src.getAbsolutePath(),
600 dst.getAbsolutePath()));
601 }
602
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700603 /**
604 * Returns the future value when complete or if future
605 * completes exceptionally returns the defaultValue.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700606 *
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700607 * @param future future
608 * @param defaultValue default value
609 * @param <T> future value type
610 * @return future value when complete or if future
611 * completes exceptionally returns the defaultValue.
612 */
613 public static <T> T futureGetOrElse(Future<T> future, T defaultValue) {
614 try {
615 return future.get();
616 } catch (InterruptedException e) {
617 Thread.currentThread().interrupt();
618 return defaultValue;
619 } catch (ExecutionException e) {
620 return defaultValue;
621 }
622 }
623
624 /**
625 * Returns the future value when complete or if future
626 * completes exceptionally returns the defaultValue.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700627 *
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700628 * @param future future
629 * @param timeout time to wait for successful completion
630 * @param timeUnit time unit
631 * @param defaultValue default value
632 * @param <T> future value type
633 * @return future value when complete or if future
634 * completes exceptionally returns the defaultValue.
635 */
636 public static <T> T futureGetOrElse(Future<T> future,
637 long timeout,
638 TimeUnit timeUnit,
639 T defaultValue) {
640 try {
641 return future.get(timeout, timeUnit);
642 } catch (InterruptedException e) {
643 Thread.currentThread().interrupt();
644 return defaultValue;
645 } catch (ExecutionException | TimeoutException e) {
646 return defaultValue;
647 }
648 }
649
Madan Jampani27b69c62015-05-15 15:49:02 -0700650 /**
651 * Returns a future that is completed exceptionally.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700652 *
Madan Jampani27b69c62015-05-15 15:49:02 -0700653 * @param t exception
654 * @param <T> future value type
655 * @return future
656 */
657 public static <T> CompletableFuture<T> exceptionalFuture(Throwable t) {
658 CompletableFuture<T> future = new CompletableFuture<>();
659 future.completeExceptionally(t);
660 return future;
661 }
662
663 /**
Jordan Halterman046faeb2017-05-01 15:10:13 -0700664 * Returns a future that's completed using the given {@code orderedExecutor} if the future is not blocked or the
665 * given {@code threadPoolExecutor} if the future is blocked.
Jordan Halterman9bdc24f2017-04-19 23:45:12 -0700666 * <p>
Jordan Halterman046faeb2017-05-01 15:10:13 -0700667 * This method allows futures to maintain single-thread semantics via the provided {@code orderedExecutor} while
668 * ensuring user code can block without blocking completion of futures. When the returned future or any of its
669 * descendants is blocked on a {@link CompletableFuture#get()} or {@link CompletableFuture#join()} call, completion
670 * of the returned future will be done using the provided {@code threadPoolExecutor}.
Jordan Halterman9bdc24f2017-04-19 23:45:12 -0700671 *
672 * @param future the future to convert into an asynchronous future
Jordan Halterman046faeb2017-05-01 15:10:13 -0700673 * @param orderedExecutor the ordered executor with which to attempt to complete the future
674 * @param threadPoolExecutor the backup executor with which to complete blocked futures
Jordan Halterman9bdc24f2017-04-19 23:45:12 -0700675 * @param <T> future value type
676 * @return a new completable future to be completed using the provided {@code executor} once the provided
677 * {@code future} is complete
678 */
Jordan Halterman046faeb2017-05-01 15:10:13 -0700679 public static <T> CompletableFuture<T> orderedFuture(
680 CompletableFuture<T> future,
681 Executor orderedExecutor,
682 Executor threadPoolExecutor) {
Jordan Haltermane265d372017-05-17 22:40:47 -0700683 if (future.isDone()) {
684 return future;
685 }
686
Yuta HIGUCHI0c47d532017-08-18 23:16:35 -0700687 BlockingAwareFuture<T> newFuture = new BlockingAwareFuture<>();
Jordan Halterman046faeb2017-05-01 15:10:13 -0700688 future.whenComplete((result, error) -> {
689 Runnable completer = () -> {
690 if (future.isCompletedExceptionally()) {
691 newFuture.completeExceptionally(error);
692 } else {
693 newFuture.complete(result);
694 }
695 };
696
697 if (newFuture.isBlocked()) {
698 threadPoolExecutor.execute(completer);
Jordan Halterman9bdc24f2017-04-19 23:45:12 -0700699 } else {
Jordan Halterman046faeb2017-05-01 15:10:13 -0700700 orderedExecutor.execute(completer);
Jordan Halterman9bdc24f2017-04-19 23:45:12 -0700701 }
Jordan Halterman046faeb2017-05-01 15:10:13 -0700702 });
Jordan Halterman9bdc24f2017-04-19 23:45:12 -0700703 return newFuture;
704 }
705
706 /**
Sho SHIMIZU85803e22016-01-13 21:53:43 -0800707 * Returns a new CompletableFuture completed with a list of computed values
708 * when all of the given CompletableFuture complete.
709 *
710 * @param futures the CompletableFutures
711 * @param <T> value type of CompletableFuture
712 * @return a new CompletableFuture that is completed when all of the given CompletableFutures complete
713 */
714 public static <T> CompletableFuture<List<T>> allOf(List<CompletableFuture<T>> futures) {
715 return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]))
716 .thenApply(v -> futures.stream()
717 .map(CompletableFuture::join)
718 .collect(Collectors.toList())
719 );
720 }
721
722 /**
Madan Jampani307a21e2016-09-01 15:49:47 -0700723 * Returns a new CompletableFuture completed by reducing a list of computed values
724 * when all of the given CompletableFuture complete.
725 *
726 * @param futures the CompletableFutures
727 * @param reducer reducer for computing the result
728 * @param emptyValue zero value to be returned if the input future list is empty
729 * @param <T> value type of CompletableFuture
730 * @return a new CompletableFuture that is completed when all of the given CompletableFutures complete
731 */
732 public static <T> CompletableFuture<T> allOf(List<CompletableFuture<T>> futures,
733 BinaryOperator<T> reducer,
734 T emptyValue) {
735 return Tools.allOf(futures)
736 .thenApply(resultList -> resultList.stream().reduce(reducer).orElse(emptyValue));
737 }
738
739 /**
740 * Returns a new CompletableFuture completed by with the first positive result from a list of
741 * input CompletableFutures.
742 *
743 * @param futures the input list of CompletableFutures
744 * @param positiveResultMatcher matcher to identify a positive result
745 * @param negativeResult value to complete with if none of the futures complete with a positive result
746 * @param <T> value type of CompletableFuture
747 * @return a new CompletableFuture
748 */
749 public static <T> CompletableFuture<T> firstOf(List<CompletableFuture<T>> futures,
750 Match<T> positiveResultMatcher,
751 T negativeResult) {
752 CompletableFuture<T> responseFuture = new CompletableFuture<>();
753 Tools.allOf(Lists.transform(futures, future -> future.thenAccept(r -> {
754 if (positiveResultMatcher.matches(r)) {
755 responseFuture.complete(r);
756 }
757 }))).whenComplete((r, e) -> {
758 if (!responseFuture.isDone()) {
759 if (e != null) {
760 responseFuture.completeExceptionally(e);
761 } else {
762 responseFuture.complete(negativeResult);
763 }
764 }
765 });
766 return responseFuture;
767 }
768
769 /**
Madan Jampani27b69c62015-05-15 15:49:02 -0700770 * Returns the contents of {@code ByteBuffer} as byte array.
771 * <p>
772 * WARNING: There is a performance cost due to array copy
773 * when using this method.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700774 *
Madan Jampani27b69c62015-05-15 15:49:02 -0700775 * @param buffer byte buffer
776 * @return byte array containing the byte buffer contents
777 */
778 public static byte[] byteBuffertoArray(ByteBuffer buffer) {
779 int length = buffer.remaining();
780 if (buffer.hasArray()) {
781 int offset = buffer.arrayOffset() + buffer.position();
782 return Arrays.copyOfRange(buffer.array(), offset, offset + length);
783 }
784 byte[] bytes = new byte[length];
785 buffer.duplicate().get(bytes);
786 return bytes;
787 }
788
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700789 /**
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700790 * Converts an iterable to a stream.
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700791 *
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700792 * @param it iterable to convert
793 * @param <T> type if item
794 * @return iterable as a stream
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700795 */
796 public static <T> Stream<T> stream(Iterable<T> it) {
797 return StreamSupport.stream(it.spliterator(), false);
798 }
799
Sho SHIMIZUb5638b82016-02-11 14:55:05 -0800800 /**
801 * Converts an optional to a stream.
802 *
803 * @param optional optional to convert
804 * @param <T> type of enclosed value
805 * @return optional as a stream
806 */
Sho SHIMIZU6ac20982016-05-04 09:50:54 -0700807 public static <T> Stream<T> stream(Optional<? extends T> optional) {
HIGUCHI Yuta0bc256f2016-05-06 15:28:26 -0700808 return optional.map(x -> Stream.<T>of(x)).orElse(Stream.empty());
Sho SHIMIZUb5638b82016-02-11 14:55:05 -0800809 }
810
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800811 // Auxiliary path visitor for recursive directory structure copying.
812 private static class DirectoryCopier extends SimpleFileVisitor<Path> {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800813 private Path src;
814 private Path dst;
815 private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING;
816
817 DirectoryCopier(String src, String dst) {
818 this.src = Paths.get(src);
819 this.dst = Paths.get(dst);
820 }
821
822 @Override
823 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
824 Path targetPath = dst.resolve(src.relativize(dir));
825 if (!Files.exists(targetPath)) {
826 Files.createDirectory(targetPath);
827 }
828 return FileVisitResult.CONTINUE;
829 }
830
831 @Override
832 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
833 Files.copy(file, dst.resolve(src.relativize(file)), copyOption);
834 return FileVisitResult.CONTINUE;
835 }
836 }
837
Yuta HIGUCHI0c47d532017-08-18 23:16:35 -0700838 /**
839 * Creates OffsetDateTime instance from epoch milliseconds,
840 * using system default time zone.
841 *
842 * @param epochMillis to convert
843 * @return OffsetDateTime
844 */
845 public static OffsetDateTime defaultOffsetDataTime(long epochMillis) {
846 return OffsetDateTime.ofInstant(Instant.ofEpochMilli(epochMillis),
847 ZoneId.systemDefault());
848 }
849
Yuta HIGUCHI47d96092017-11-17 14:05:26 -0800850 /**
851 * Returns smaller of the two Comparable values.
852 *
853 * @param l an argument
854 * @param r another argument
855 * @return the smaller of {@code l} or {@code r}
856 * @param <C> Comparable type
857 * @throws NullPointerException if any of the arguments were null.
858 */
859 public static <C extends Comparable<? super C>> C min(C l, C r) {
860 checkNotNull(l, "l cannot be null");
861 checkNotNull(r, "r cannot be null");
862 return l.compareTo(r) <= 0 ? l : r;
863 }
864
865 /**
866 * Returns larger of the two Comparable values.
867 *
868 * @param l an argument
869 * @param r another argument
870 * @return the larger of {@code l} or {@code r}
871 * @param <C> Comparable type
872 * @throws NullPointerException if any of the arguments were null.
873 */
874 public static <C extends Comparable<? super C>> C max(C l, C r) {
875 checkNotNull(l, "l cannot be null");
876 checkNotNull(r, "r cannot be null");
877 return l.compareTo(r) >= 0 ? l : r;
878 }
tom5f38b3a2014-08-27 23:50:54 -0700879}