blob: bff3953c554216bf99cdbfb9df0c1f5e3fea80e5 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 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;
28import java.nio.charset.StandardCharsets;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080029import java.nio.file.FileVisitResult;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080030import java.nio.file.Files;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080031import java.nio.file.Path;
32import java.nio.file.Paths;
33import java.nio.file.SimpleFileVisitor;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080034import java.nio.file.StandardCopyOption;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080035import java.nio.file.attribute.BasicFileAttributes;
tom53efab52014-10-07 17:43:48 -070036import java.util.ArrayList;
Brian O'Connore2eac102015-02-12 18:30:22 -080037import java.util.Collection;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070038import java.util.Dictionary;
tom53efab52014-10-07 17:43:48 -070039import java.util.List;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070040import java.util.concurrent.ExecutionException;
41import java.util.concurrent.Future;
tom5f38b3a2014-08-27 23:50:54 -070042import java.util.concurrent.ThreadFactory;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070043import java.util.concurrent.TimeUnit;
44import java.util.concurrent.TimeoutException;
tom5f38b3a2014-08-27 23:50:54 -070045
Madan Jampani2bfa94c2015-04-11 05:03:49 -070046import org.slf4j.Logger;
47
48import com.google.common.base.Strings;
49import com.google.common.primitives.UnsignedLongs;
50import com.google.common.util.concurrent.ThreadFactoryBuilder;
Ray Milkey705d9bc2014-11-18 08:19:00 -080051
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080052/**
53 * Miscellaneous utility methods.
54 */
tom5f38b3a2014-08-27 23:50:54 -070055public abstract class Tools {
56
57 private Tools() {
58 }
59
Thomas Vachuska02aeb032015-01-06 22:36:30 -080060 private static final Logger log = getLogger(Tools.class);
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080061
tom5f38b3a2014-08-27 23:50:54 -070062 /**
63 * Returns a thread factory that produces threads named according to the
64 * supplied name pattern.
65 *
66 * @param pattern name pattern
67 * @return thread factory
68 */
69 public static ThreadFactory namedThreads(String pattern) {
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080070 return new ThreadFactoryBuilder()
71 .setNameFormat(pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080072 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
73 .build();
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080074 }
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080075
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080076 /**
77 * Returns a thread factory that produces threads named according to the
78 * supplied name pattern and from the specified thread-group. The thread
79 * group name is expected to be specified in slash-delimited format, e.g.
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080080 * {@code onos/intent}. The thread names will be produced by converting
81 * the thread group name into dash-delimited format and pre-pended to the
82 * specified pattern.
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080083 *
84 * @param groupName group name in slash-delimited format to indicate hierarchy
85 * @param pattern name pattern
86 * @return thread factory
87 */
88 public static ThreadFactory groupedThreads(String groupName, String pattern) {
89 return new ThreadFactoryBuilder()
90 .setThreadFactory(groupedThreadFactory(groupName))
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080091 .setNameFormat(groupName.replace(GroupedThreadFactory.DELIMITER, "-") + "-" + pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080092 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
93 .build();
tom5f38b3a2014-08-27 23:50:54 -070094 }
95
tom782a7cf2014-09-11 23:58:38 -070096 /**
Yuta HIGUCHI06586272014-11-25 14:27:03 -080097 * Returns a thread factory that produces threads with MIN_PRIORITY.
98 *
99 * @param factory backing ThreadFactory
100 * @return thread factory
101 */
102 public static ThreadFactory minPriority(ThreadFactory factory) {
103 return new ThreadFactoryBuilder()
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800104 .setThreadFactory(factory)
105 .setPriority(Thread.MIN_PRIORITY)
106 .build();
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800107 }
108
109 /**
Brian O'Connore2eac102015-02-12 18:30:22 -0800110 * Returns true if the collection is null or is empty.
111 *
112 * @param collection collection to test
113 * @return true if null or empty; false otherwise
114 */
115 public static boolean isNullOrEmpty(Collection collection) {
116 return collection == null || collection.isEmpty();
117 }
118
119 /**
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700120 * Returns the specified item if that items is null; otherwise throws
121 * not found exception.
122 *
123 * @param item item to check
124 * @param message not found message
125 * @param <T> item type
126 * @return item if not null
127 * @throws org.onlab.util.ItemNotFoundException if item is null
128 */
129 public static <T> T nullIsNotFound(T item, String message) {
130 if (item == null) {
131 throw new ItemNotFoundException(message);
132 }
133 return item;
134 }
135
136 /**
tom782a7cf2014-09-11 23:58:38 -0700137 * Converts a string from hex to long.
138 *
139 * @param string hex number in string form; sans 0x
140 * @return long value
141 */
142 public static long fromHex(String string) {
143 return UnsignedLongs.parseUnsignedLong(string, 16);
144 }
145
146 /**
147 * Converts a long value to hex string; 16 wide and sans 0x.
148 *
149 * @param value long value
150 * @return hex string
151 */
152 public static String toHex(long value) {
153 return Strings.padStart(UnsignedLongs.toString(value, 16), 16, '0');
154 }
155
156 /**
157 * Converts a long value to hex string; 16 wide and sans 0x.
158 *
159 * @param value long value
160 * @param width string width; zero padded
161 * @return hex string
162 */
163 public static String toHex(long value, int width) {
164 return Strings.padStart(UnsignedLongs.toString(value, 16), width, '0');
165 }
tomf110fff2014-09-26 00:38:18 -0700166
167 /**
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700168 * Get property as a string value.
169 *
170 * @param properties properties to be looked up
171 * @param propertyName the name of the property to look up
172 * @return value when the propertyName is defined or return null
173 */
174 public static String get(Dictionary<?, ?> properties, String propertyName) {
175 Object v = properties.get(propertyName);
176 String s = (v instanceof String) ? (String) v :
177 v != null ? v.toString() : null;
178 return Strings.isNullOrEmpty(s) ? null : s.trim();
179 }
180
181 /**
tomf110fff2014-09-26 00:38:18 -0700182 * Suspends the current thread for a specified number of millis.
183 *
184 * @param ms number of millis
185 */
186 public static void delay(int ms) {
187 try {
188 Thread.sleep(ms);
189 } catch (InterruptedException e) {
190 throw new RuntimeException("Interrupted", e);
191 }
192 }
193
tom53efab52014-10-07 17:43:48 -0700194 /**
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700195 * Suspends the current thread for a specified number of millis and nanos.
196 *
197 * @param ms number of millis
198 * @param nanos number of nanos
199 */
200 public static void delay(int ms, int nanos) {
201 try {
202 Thread.sleep(ms, nanos);
203 } catch (InterruptedException e) {
204 throw new RuntimeException("Interrupted", e);
205 }
206 }
207
208 /**
tom53efab52014-10-07 17:43:48 -0700209 * Slurps the contents of a file into a list of strings, one per line.
210 *
211 * @param path file path
212 * @return file contents
213 */
214 public static List<String> slurp(File path) {
Ray Milkey705d9bc2014-11-18 08:19:00 -0800215 try {
216 BufferedReader br = new BufferedReader(
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800217 new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8));
Ray Milkey705d9bc2014-11-18 08:19:00 -0800218
tom53efab52014-10-07 17:43:48 -0700219 List<String> lines = new ArrayList<>();
220 String line;
221 while ((line = br.readLine()) != null) {
222 lines.add(line);
223 }
224 return lines;
225
226 } catch (IOException e) {
227 return null;
228 }
229 }
230
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800231
232 /**
233 * Purges the specified directory path.&nbsp;Use with great caution since
234 * no attempt is made to check for symbolic links, which could result in
235 * deletion of unintended files.
236 *
237 * @param path directory to be removed
238 * @throws java.io.IOException if unable to remove contents
239 */
240 public static void removeDirectory(String path) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800241 DirectoryDeleter visitor = new DirectoryDeleter();
242 walkFileTree(Paths.get(path), visitor);
243 if (visitor.exception != null) {
244 throw visitor.exception;
245 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800246 }
247
248 /**
249 * Purges the specified directory path.&nbsp;Use with great caution since
250 * no attempt is made to check for symbolic links, which could result in
251 * deletion of unintended files.
252 *
253 * @param dir directory to be removed
254 * @throws java.io.IOException if unable to remove contents
255 */
256 public static void removeDirectory(File dir) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800257 DirectoryDeleter visitor = new DirectoryDeleter();
258 walkFileTree(Paths.get(dir.getAbsolutePath()), visitor);
259 if (visitor.exception != null) {
260 throw visitor.exception;
261 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800262 }
263
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800264 // Auxiliary path visitor for recursive directory structure removal.
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800265 private static class DirectoryDeleter extends SimpleFileVisitor<Path> {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800266
267 private IOException exception;
268
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800269 @Override
270 public FileVisitResult visitFile(Path file, BasicFileAttributes attributes)
271 throws IOException {
272 if (attributes.isRegularFile()) {
273 delete(file);
274 }
275 return FileVisitResult.CONTINUE;
276 }
277
278 @Override
279 public FileVisitResult postVisitDirectory(Path directory, IOException ioe)
280 throws IOException {
281 delete(directory);
282 return FileVisitResult.CONTINUE;
283 }
284
285 @Override
286 public FileVisitResult visitFileFailed(Path file, IOException ioe)
287 throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800288 this.exception = ioe;
289 return FileVisitResult.TERMINATE;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800290 }
291 }
292
Madan Jampani30a57f82015-03-02 12:19:41 -0800293 /**
294 * Returns a human friendly time ago string for a specified system time.
Thomas Vachuska480adad2015-03-06 10:27:09 -0800295 *
Madan Jampani30a57f82015-03-02 12:19:41 -0800296 * @param unixTime system time in millis
297 * @return human friendly time ago
298 */
299 public static String timeAgo(long unixTime) {
300 long deltaMillis = System.currentTimeMillis() - unixTime;
301 long secondsSince = (long) (deltaMillis / 1000.0);
302 long minsSince = (long) (deltaMillis / (1000.0 * 60));
303 long hoursSince = (long) (deltaMillis / (1000.0 * 60 * 60));
304 long daysSince = (long) (deltaMillis / (1000.0 * 60 * 60 * 24));
305 if (daysSince > 0) {
306 return String.format("%dd ago", daysSince);
307 } else if (hoursSince > 0) {
308 return String.format("%dh ago", hoursSince);
309 } else if (minsSince > 0) {
310 return String.format("%dm ago", minsSince);
311 } else if (secondsSince > 0) {
312 return String.format("%ds ago", secondsSince);
313 } else {
314 return "just now";
315 }
316 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800317
318 /**
319 * Copies the specified directory path.&nbsp;Use with great caution since
320 * no attempt is made to check for symbolic links, which could result in
321 * copy of unintended files.
322 *
323 * @param src directory to be copied
324 * @param dst destination directory to be removed
325 * @throws java.io.IOException if unable to remove contents
326 */
327 public static void copyDirectory(String src, String dst) throws IOException {
328 walkFileTree(Paths.get(src), new DirectoryCopier(src, dst));
329 }
330
331 /**
332 * Copies the specified directory path.&nbsp;Use with great caution since
333 * no attempt is made to check for symbolic links, which could result in
334 * copy of unintended files.
335 *
336 * @param src directory to be copied
337 * @param dst destination directory to be removed
338 * @throws java.io.IOException if unable to remove contents
339 */
340 public static void copyDirectory(File src, File dst) throws IOException {
341 walkFileTree(Paths.get(src.getAbsolutePath()),
342 new DirectoryCopier(src.getAbsolutePath(),
343 dst.getAbsolutePath()));
344 }
345
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700346 /**
347 * Returns the future value when complete or if future
348 * completes exceptionally returns the defaultValue.
349 * @param future future
350 * @param defaultValue default value
351 * @param <T> future value type
352 * @return future value when complete or if future
353 * completes exceptionally returns the defaultValue.
354 */
355 public static <T> T futureGetOrElse(Future<T> future, T defaultValue) {
356 try {
357 return future.get();
358 } catch (InterruptedException e) {
359 Thread.currentThread().interrupt();
360 return defaultValue;
361 } catch (ExecutionException e) {
362 return defaultValue;
363 }
364 }
365
366 /**
367 * Returns the future value when complete or if future
368 * completes exceptionally returns the defaultValue.
369 * @param future future
370 * @param timeout time to wait for successful completion
371 * @param timeUnit time unit
372 * @param defaultValue default value
373 * @param <T> future value type
374 * @return future value when complete or if future
375 * completes exceptionally returns the defaultValue.
376 */
377 public static <T> T futureGetOrElse(Future<T> future,
378 long timeout,
379 TimeUnit timeUnit,
380 T defaultValue) {
381 try {
382 return future.get(timeout, timeUnit);
383 } catch (InterruptedException e) {
384 Thread.currentThread().interrupt();
385 return defaultValue;
386 } catch (ExecutionException | TimeoutException e) {
387 return defaultValue;
388 }
389 }
390
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800391 // Auxiliary path visitor for recursive directory structure copying.
392 private static class DirectoryCopier extends SimpleFileVisitor<Path> {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800393 private Path src;
394 private Path dst;
395 private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING;
396
397 DirectoryCopier(String src, String dst) {
398 this.src = Paths.get(src);
399 this.dst = Paths.get(dst);
400 }
401
402 @Override
403 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
404 Path targetPath = dst.resolve(src.relativize(dir));
405 if (!Files.exists(targetPath)) {
406 Files.createDirectory(targetPath);
407 }
408 return FileVisitResult.CONTINUE;
409 }
410
411 @Override
412 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
413 Files.copy(file, dst.resolve(src.relativize(file)), copyOption);
414 return FileVisitResult.CONTINUE;
415 }
416 }
417
tom5f38b3a2014-08-27 23:50:54 -0700418}