blob: d9eb111ff0e6adaccdd78752684cc1a920d85764 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska24c849c2014-10-27 09:53:05 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska24c849c2014-10-27 09:53:05 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska24c849c2014-10-27 09:53:05 -070015 */
tom5f38b3a2014-08-27 23:50:54 -070016package org.onlab.util;
17
Madan Jampani2bfa94c2015-04-11 05:03:49 -070018import static java.nio.file.Files.delete;
19import static java.nio.file.Files.walkFileTree;
20import static org.onlab.util.GroupedThreadFactory.groupedThreadFactory;
21import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080022
tom53efab52014-10-07 17:43:48 -070023import java.io.BufferedReader;
24import java.io.File;
Ray Milkey705d9bc2014-11-18 08:19:00 -080025import java.io.FileInputStream;
tom53efab52014-10-07 17:43:48 -070026import java.io.IOException;
Ray Milkey705d9bc2014-11-18 08:19:00 -080027import java.io.InputStreamReader;
Madan Jampani27b69c62015-05-15 15:49:02 -070028import java.nio.ByteBuffer;
Ray Milkey705d9bc2014-11-18 08:19:00 -080029import java.nio.charset.StandardCharsets;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080030import java.nio.file.FileVisitResult;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080031import java.nio.file.Files;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080032import java.nio.file.Path;
33import java.nio.file.Paths;
34import java.nio.file.SimpleFileVisitor;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080035import java.nio.file.StandardCopyOption;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080036import java.nio.file.attribute.BasicFileAttributes;
tom53efab52014-10-07 17:43:48 -070037import java.util.ArrayList;
Madan Jampani27b69c62015-05-15 15:49:02 -070038import java.util.Arrays;
Brian O'Connore2eac102015-02-12 18:30:22 -080039import java.util.Collection;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070040import java.util.Dictionary;
tom53efab52014-10-07 17:43:48 -070041import java.util.List;
Madan Jampani27b69c62015-05-15 15:49:02 -070042import java.util.concurrent.CompletableFuture;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070043import java.util.concurrent.ExecutionException;
44import java.util.concurrent.Future;
tom5f38b3a2014-08-27 23:50:54 -070045import java.util.concurrent.ThreadFactory;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070046import java.util.concurrent.TimeUnit;
47import java.util.concurrent.TimeoutException;
tom5f38b3a2014-08-27 23:50:54 -070048
Madan Jampani2bfa94c2015-04-11 05:03:49 -070049import org.slf4j.Logger;
50
51import com.google.common.base.Strings;
52import com.google.common.primitives.UnsignedLongs;
53import com.google.common.util.concurrent.ThreadFactoryBuilder;
Ray Milkey705d9bc2014-11-18 08:19:00 -080054
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080055/**
56 * Miscellaneous utility methods.
57 */
tom5f38b3a2014-08-27 23:50:54 -070058public abstract class Tools {
59
60 private Tools() {
61 }
62
Thomas Vachuska02aeb032015-01-06 22:36:30 -080063 private static final Logger log = getLogger(Tools.class);
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080064
tom5f38b3a2014-08-27 23:50:54 -070065 /**
66 * Returns a thread factory that produces threads named according to the
67 * supplied name pattern.
68 *
69 * @param pattern name pattern
70 * @return thread factory
71 */
72 public static ThreadFactory namedThreads(String pattern) {
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080073 return new ThreadFactoryBuilder()
74 .setNameFormat(pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080075 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
76 .build();
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080077 }
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080078
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080079 /**
80 * Returns a thread factory that produces threads named according to the
81 * supplied name pattern and from the specified thread-group. The thread
82 * group name is expected to be specified in slash-delimited format, e.g.
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080083 * {@code onos/intent}. The thread names will be produced by converting
84 * the thread group name into dash-delimited format and pre-pended to the
85 * specified pattern.
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080086 *
87 * @param groupName group name in slash-delimited format to indicate hierarchy
88 * @param pattern name pattern
89 * @return thread factory
90 */
91 public static ThreadFactory groupedThreads(String groupName, String pattern) {
92 return new ThreadFactoryBuilder()
93 .setThreadFactory(groupedThreadFactory(groupName))
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080094 .setNameFormat(groupName.replace(GroupedThreadFactory.DELIMITER, "-") + "-" + pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080095 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
96 .build();
tom5f38b3a2014-08-27 23:50:54 -070097 }
98
tom782a7cf2014-09-11 23:58:38 -070099 /**
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800100 * Returns a thread factory that produces threads with MIN_PRIORITY.
101 *
102 * @param factory backing ThreadFactory
103 * @return thread factory
104 */
105 public static ThreadFactory minPriority(ThreadFactory factory) {
106 return new ThreadFactoryBuilder()
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800107 .setThreadFactory(factory)
108 .setPriority(Thread.MIN_PRIORITY)
109 .build();
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800110 }
111
112 /**
Brian O'Connore2eac102015-02-12 18:30:22 -0800113 * Returns true if the collection is null or is empty.
114 *
115 * @param collection collection to test
116 * @return true if null or empty; false otherwise
117 */
118 public static boolean isNullOrEmpty(Collection collection) {
119 return collection == null || collection.isEmpty();
120 }
121
122 /**
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700123 * Returns the specified item if that items is null; otherwise throws
124 * not found exception.
125 *
126 * @param item item to check
127 * @param message not found message
128 * @param <T> item type
129 * @return item if not null
130 * @throws org.onlab.util.ItemNotFoundException if item is null
131 */
132 public static <T> T nullIsNotFound(T item, String message) {
133 if (item == null) {
134 throw new ItemNotFoundException(message);
135 }
136 return item;
137 }
138
139 /**
tom782a7cf2014-09-11 23:58:38 -0700140 * Converts a string from hex to long.
141 *
142 * @param string hex number in string form; sans 0x
143 * @return long value
144 */
145 public static long fromHex(String string) {
146 return UnsignedLongs.parseUnsignedLong(string, 16);
147 }
148
149 /**
150 * Converts a long value to hex string; 16 wide and sans 0x.
151 *
152 * @param value long value
153 * @return hex string
154 */
155 public static String toHex(long value) {
156 return Strings.padStart(UnsignedLongs.toString(value, 16), 16, '0');
157 }
158
159 /**
160 * Converts a long value to hex string; 16 wide and sans 0x.
161 *
162 * @param value long value
163 * @param width string width; zero padded
164 * @return hex string
165 */
166 public static String toHex(long value, int width) {
167 return Strings.padStart(UnsignedLongs.toString(value, 16), width, '0');
168 }
tomf110fff2014-09-26 00:38:18 -0700169
170 /**
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700171 * Get property as a string value.
172 *
173 * @param properties properties to be looked up
174 * @param propertyName the name of the property to look up
175 * @return value when the propertyName is defined or return null
176 */
177 public static String get(Dictionary<?, ?> properties, String propertyName) {
178 Object v = properties.get(propertyName);
179 String s = (v instanceof String) ? (String) v :
180 v != null ? v.toString() : null;
181 return Strings.isNullOrEmpty(s) ? null : s.trim();
182 }
183
184 /**
tomf110fff2014-09-26 00:38:18 -0700185 * Suspends the current thread for a specified number of millis.
186 *
187 * @param ms number of millis
188 */
189 public static void delay(int ms) {
190 try {
191 Thread.sleep(ms);
192 } catch (InterruptedException e) {
193 throw new RuntimeException("Interrupted", e);
194 }
195 }
196
tom53efab52014-10-07 17:43:48 -0700197 /**
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700198 * Suspends the current thread for a specified number of millis and nanos.
199 *
200 * @param ms number of millis
201 * @param nanos number of nanos
202 */
203 public static void delay(int ms, int nanos) {
204 try {
205 Thread.sleep(ms, nanos);
206 } catch (InterruptedException e) {
207 throw new RuntimeException("Interrupted", e);
208 }
209 }
210
211 /**
tom53efab52014-10-07 17:43:48 -0700212 * Slurps the contents of a file into a list of strings, one per line.
213 *
214 * @param path file path
215 * @return file contents
216 */
217 public static List<String> slurp(File path) {
Ray Milkey705d9bc2014-11-18 08:19:00 -0800218 try {
219 BufferedReader br = new BufferedReader(
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800220 new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8));
Ray Milkey705d9bc2014-11-18 08:19:00 -0800221
tom53efab52014-10-07 17:43:48 -0700222 List<String> lines = new ArrayList<>();
223 String line;
224 while ((line = br.readLine()) != null) {
225 lines.add(line);
226 }
227 return lines;
228
229 } catch (IOException e) {
230 return null;
231 }
232 }
233
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800234 /**
235 * Purges the specified directory path.&nbsp;Use with great caution since
236 * no attempt is made to check for symbolic links, which could result in
237 * deletion of unintended files.
238 *
239 * @param path directory to be removed
240 * @throws java.io.IOException if unable to remove contents
241 */
242 public static void removeDirectory(String path) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800243 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700244 File dir = new File(path);
245 if (dir.exists() && dir.isDirectory()) {
246 walkFileTree(Paths.get(path), visitor);
247 if (visitor.exception != null) {
248 throw visitor.exception;
249 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800250 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800251 }
252
253 /**
254 * Purges the specified directory path.&nbsp;Use with great caution since
255 * no attempt is made to check for symbolic links, which could result in
256 * deletion of unintended files.
257 *
258 * @param dir directory to be removed
259 * @throws java.io.IOException if unable to remove contents
260 */
261 public static void removeDirectory(File dir) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800262 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700263 if (dir.exists() && dir.isDirectory()) {
264 walkFileTree(Paths.get(dir.getAbsolutePath()), visitor);
265 if (visitor.exception != null) {
266 throw visitor.exception;
267 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800268 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800269 }
270
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800271 // Auxiliary path visitor for recursive directory structure removal.
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800272 private static class DirectoryDeleter extends SimpleFileVisitor<Path> {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800273
274 private IOException exception;
275
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800276 @Override
277 public FileVisitResult visitFile(Path file, BasicFileAttributes attributes)
278 throws IOException {
279 if (attributes.isRegularFile()) {
280 delete(file);
281 }
282 return FileVisitResult.CONTINUE;
283 }
284
285 @Override
286 public FileVisitResult postVisitDirectory(Path directory, IOException ioe)
287 throws IOException {
288 delete(directory);
289 return FileVisitResult.CONTINUE;
290 }
291
292 @Override
293 public FileVisitResult visitFileFailed(Path file, IOException ioe)
294 throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800295 this.exception = ioe;
296 return FileVisitResult.TERMINATE;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800297 }
298 }
299
Madan Jampani30a57f82015-03-02 12:19:41 -0800300 /**
301 * Returns a human friendly time ago string for a specified system time.
Thomas Vachuska480adad2015-03-06 10:27:09 -0800302 *
Madan Jampani30a57f82015-03-02 12:19:41 -0800303 * @param unixTime system time in millis
304 * @return human friendly time ago
305 */
306 public static String timeAgo(long unixTime) {
307 long deltaMillis = System.currentTimeMillis() - unixTime;
308 long secondsSince = (long) (deltaMillis / 1000.0);
309 long minsSince = (long) (deltaMillis / (1000.0 * 60));
310 long hoursSince = (long) (deltaMillis / (1000.0 * 60 * 60));
311 long daysSince = (long) (deltaMillis / (1000.0 * 60 * 60 * 24));
312 if (daysSince > 0) {
313 return String.format("%dd ago", daysSince);
314 } else if (hoursSince > 0) {
315 return String.format("%dh ago", hoursSince);
316 } else if (minsSince > 0) {
317 return String.format("%dm ago", minsSince);
318 } else if (secondsSince > 0) {
319 return String.format("%ds ago", secondsSince);
320 } else {
321 return "just now";
322 }
323 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800324
325 /**
326 * Copies the specified directory path.&nbsp;Use with great caution since
327 * no attempt is made to check for symbolic links, which could result in
328 * copy of unintended files.
329 *
330 * @param src directory to be copied
331 * @param dst destination directory to be removed
332 * @throws java.io.IOException if unable to remove contents
333 */
334 public static void copyDirectory(String src, String dst) throws IOException {
335 walkFileTree(Paths.get(src), new DirectoryCopier(src, dst));
336 }
337
338 /**
339 * Copies the specified directory path.&nbsp;Use with great caution since
340 * no attempt is made to check for symbolic links, which could result in
341 * copy of unintended files.
342 *
343 * @param src directory to be copied
344 * @param dst destination directory to be removed
345 * @throws java.io.IOException if unable to remove contents
346 */
347 public static void copyDirectory(File src, File dst) throws IOException {
348 walkFileTree(Paths.get(src.getAbsolutePath()),
349 new DirectoryCopier(src.getAbsolutePath(),
350 dst.getAbsolutePath()));
351 }
352
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700353 /**
354 * Returns the future value when complete or if future
355 * completes exceptionally returns the defaultValue.
356 * @param future future
357 * @param defaultValue default value
358 * @param <T> future value type
359 * @return future value when complete or if future
360 * completes exceptionally returns the defaultValue.
361 */
362 public static <T> T futureGetOrElse(Future<T> future, T defaultValue) {
363 try {
364 return future.get();
365 } catch (InterruptedException e) {
366 Thread.currentThread().interrupt();
367 return defaultValue;
368 } catch (ExecutionException e) {
369 return defaultValue;
370 }
371 }
372
373 /**
374 * Returns the future value when complete or if future
375 * completes exceptionally returns the defaultValue.
376 * @param future future
377 * @param timeout time to wait for successful completion
378 * @param timeUnit time unit
379 * @param defaultValue default value
380 * @param <T> future value type
381 * @return future value when complete or if future
382 * completes exceptionally returns the defaultValue.
383 */
384 public static <T> T futureGetOrElse(Future<T> future,
385 long timeout,
386 TimeUnit timeUnit,
387 T defaultValue) {
388 try {
389 return future.get(timeout, timeUnit);
390 } catch (InterruptedException e) {
391 Thread.currentThread().interrupt();
392 return defaultValue;
393 } catch (ExecutionException | TimeoutException e) {
394 return defaultValue;
395 }
396 }
397
Madan Jampani27b69c62015-05-15 15:49:02 -0700398 /**
399 * Returns a future that is completed exceptionally.
400 * @param t exception
401 * @param <T> future value type
402 * @return future
403 */
404 public static <T> CompletableFuture<T> exceptionalFuture(Throwable t) {
405 CompletableFuture<T> future = new CompletableFuture<>();
406 future.completeExceptionally(t);
407 return future;
408 }
409
410 /**
411 * Returns the contents of {@code ByteBuffer} as byte array.
412 * <p>
413 * WARNING: There is a performance cost due to array copy
414 * when using this method.
415 * @param buffer byte buffer
416 * @return byte array containing the byte buffer contents
417 */
418 public static byte[] byteBuffertoArray(ByteBuffer buffer) {
419 int length = buffer.remaining();
420 if (buffer.hasArray()) {
421 int offset = buffer.arrayOffset() + buffer.position();
422 return Arrays.copyOfRange(buffer.array(), offset, offset + length);
423 }
424 byte[] bytes = new byte[length];
425 buffer.duplicate().get(bytes);
426 return bytes;
427 }
428
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800429 // Auxiliary path visitor for recursive directory structure copying.
430 private static class DirectoryCopier extends SimpleFileVisitor<Path> {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800431 private Path src;
432 private Path dst;
433 private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING;
434
435 DirectoryCopier(String src, String dst) {
436 this.src = Paths.get(src);
437 this.dst = Paths.get(dst);
438 }
439
440 @Override
441 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
442 Path targetPath = dst.resolve(src.relativize(dir));
443 if (!Files.exists(targetPath)) {
444 Files.createDirectory(targetPath);
445 }
446 return FileVisitResult.CONTINUE;
447 }
448
449 @Override
450 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
451 Files.copy(file, dst.resolve(src.relativize(file)), copyOption);
452 return FileVisitResult.CONTINUE;
453 }
454 }
455
tom5f38b3a2014-08-27 23:50:54 -0700456}