blob: 01dda9e5b6b784d250bd7d022ad35f243b8329bf [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
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080018import com.google.common.base.Strings;
19import com.google.common.primitives.UnsignedLongs;
20import com.google.common.util.concurrent.ThreadFactoryBuilder;
21import org.slf4j.Logger;
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;
tom5f38b3a2014-08-27 23:50:54 -070040import java.util.concurrent.ThreadFactory;
41
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080042import static java.nio.file.Files.delete;
43import static java.nio.file.Files.walkFileTree;
44import static org.onlab.util.GroupedThreadFactory.groupedThreadFactory;
45import static org.slf4j.LoggerFactory.getLogger;
Ray Milkey705d9bc2014-11-18 08:19:00 -080046
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080047/**
48 * Miscellaneous utility methods.
49 */
tom5f38b3a2014-08-27 23:50:54 -070050public abstract class Tools {
51
52 private Tools() {
53 }
54
Thomas Vachuska02aeb032015-01-06 22:36:30 -080055 private static final Logger log = getLogger(Tools.class);
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080056
tom5f38b3a2014-08-27 23:50:54 -070057 /**
58 * Returns a thread factory that produces threads named according to the
59 * supplied name pattern.
60 *
61 * @param pattern name pattern
62 * @return thread factory
63 */
64 public static ThreadFactory namedThreads(String pattern) {
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080065 return new ThreadFactoryBuilder()
66 .setNameFormat(pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080067 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
68 .build();
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080069 }
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080070
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080071 /**
72 * Returns a thread factory that produces threads named according to the
73 * supplied name pattern and from the specified thread-group. The thread
74 * group name is expected to be specified in slash-delimited format, e.g.
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080075 * {@code onos/intent}. The thread names will be produced by converting
76 * the thread group name into dash-delimited format and pre-pended to the
77 * specified pattern.
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080078 *
79 * @param groupName group name in slash-delimited format to indicate hierarchy
80 * @param pattern name pattern
81 * @return thread factory
82 */
83 public static ThreadFactory groupedThreads(String groupName, String pattern) {
84 return new ThreadFactoryBuilder()
85 .setThreadFactory(groupedThreadFactory(groupName))
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080086 .setNameFormat(groupName.replace(GroupedThreadFactory.DELIMITER, "-") + "-" + pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080087 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
88 .build();
tom5f38b3a2014-08-27 23:50:54 -070089 }
90
tom782a7cf2014-09-11 23:58:38 -070091 /**
Yuta HIGUCHI06586272014-11-25 14:27:03 -080092 * Returns a thread factory that produces threads with MIN_PRIORITY.
93 *
94 * @param factory backing ThreadFactory
95 * @return thread factory
96 */
97 public static ThreadFactory minPriority(ThreadFactory factory) {
98 return new ThreadFactoryBuilder()
Thomas Vachuska02aeb032015-01-06 22:36:30 -080099 .setThreadFactory(factory)
100 .setPriority(Thread.MIN_PRIORITY)
101 .build();
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800102 }
103
104 /**
Brian O'Connore2eac102015-02-12 18:30:22 -0800105 * Returns true if the collection is null or is empty.
106 *
107 * @param collection collection to test
108 * @return true if null or empty; false otherwise
109 */
110 public static boolean isNullOrEmpty(Collection collection) {
111 return collection == null || collection.isEmpty();
112 }
113
114 /**
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700115 * Returns the specified item if that items is null; otherwise throws
116 * not found exception.
117 *
118 * @param item item to check
119 * @param message not found message
120 * @param <T> item type
121 * @return item if not null
122 * @throws org.onlab.util.ItemNotFoundException if item is null
123 */
124 public static <T> T nullIsNotFound(T item, String message) {
125 if (item == null) {
126 throw new ItemNotFoundException(message);
127 }
128 return item;
129 }
130
131 /**
tom782a7cf2014-09-11 23:58:38 -0700132 * Converts a string from hex to long.
133 *
134 * @param string hex number in string form; sans 0x
135 * @return long value
136 */
137 public static long fromHex(String string) {
138 return UnsignedLongs.parseUnsignedLong(string, 16);
139 }
140
141 /**
142 * Converts a long value to hex string; 16 wide and sans 0x.
143 *
144 * @param value long value
145 * @return hex string
146 */
147 public static String toHex(long value) {
148 return Strings.padStart(UnsignedLongs.toString(value, 16), 16, '0');
149 }
150
151 /**
152 * Converts a long value to hex string; 16 wide and sans 0x.
153 *
154 * @param value long value
155 * @param width string width; zero padded
156 * @return hex string
157 */
158 public static String toHex(long value, int width) {
159 return Strings.padStart(UnsignedLongs.toString(value, 16), width, '0');
160 }
tomf110fff2014-09-26 00:38:18 -0700161
162 /**
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700163 * Get property as a string value.
164 *
165 * @param properties properties to be looked up
166 * @param propertyName the name of the property to look up
167 * @return value when the propertyName is defined or return null
168 */
169 public static String get(Dictionary<?, ?> properties, String propertyName) {
170 Object v = properties.get(propertyName);
171 String s = (v instanceof String) ? (String) v :
172 v != null ? v.toString() : null;
173 return Strings.isNullOrEmpty(s) ? null : s.trim();
174 }
175
176 /**
tomf110fff2014-09-26 00:38:18 -0700177 * Suspends the current thread for a specified number of millis.
178 *
179 * @param ms number of millis
180 */
181 public static void delay(int ms) {
182 try {
183 Thread.sleep(ms);
184 } catch (InterruptedException e) {
185 throw new RuntimeException("Interrupted", e);
186 }
187 }
188
tom53efab52014-10-07 17:43:48 -0700189 /**
190 * Slurps the contents of a file into a list of strings, one per line.
191 *
192 * @param path file path
193 * @return file contents
194 */
195 public static List<String> slurp(File path) {
Ray Milkey705d9bc2014-11-18 08:19:00 -0800196 try {
197 BufferedReader br = new BufferedReader(
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800198 new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8));
Ray Milkey705d9bc2014-11-18 08:19:00 -0800199
tom53efab52014-10-07 17:43:48 -0700200 List<String> lines = new ArrayList<>();
201 String line;
202 while ((line = br.readLine()) != null) {
203 lines.add(line);
204 }
205 return lines;
206
207 } catch (IOException e) {
208 return null;
209 }
210 }
211
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800212
213 /**
214 * Purges the specified directory path.&nbsp;Use with great caution since
215 * no attempt is made to check for symbolic links, which could result in
216 * deletion of unintended files.
217 *
218 * @param path directory to be removed
219 * @throws java.io.IOException if unable to remove contents
220 */
221 public static void removeDirectory(String path) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800222 DirectoryDeleter visitor = new DirectoryDeleter();
223 walkFileTree(Paths.get(path), visitor);
224 if (visitor.exception != null) {
225 throw visitor.exception;
226 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800227 }
228
229 /**
230 * Purges the specified directory path.&nbsp;Use with great caution since
231 * no attempt is made to check for symbolic links, which could result in
232 * deletion of unintended files.
233 *
234 * @param dir directory to be removed
235 * @throws java.io.IOException if unable to remove contents
236 */
237 public static void removeDirectory(File dir) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800238 DirectoryDeleter visitor = new DirectoryDeleter();
239 walkFileTree(Paths.get(dir.getAbsolutePath()), visitor);
240 if (visitor.exception != null) {
241 throw visitor.exception;
242 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800243 }
244
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800245 // Auxiliary path visitor for recursive directory structure removal.
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800246 private static class DirectoryDeleter extends SimpleFileVisitor<Path> {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800247
248 private IOException exception;
249
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800250 @Override
251 public FileVisitResult visitFile(Path file, BasicFileAttributes attributes)
252 throws IOException {
253 if (attributes.isRegularFile()) {
254 delete(file);
255 }
256 return FileVisitResult.CONTINUE;
257 }
258
259 @Override
260 public FileVisitResult postVisitDirectory(Path directory, IOException ioe)
261 throws IOException {
262 delete(directory);
263 return FileVisitResult.CONTINUE;
264 }
265
266 @Override
267 public FileVisitResult visitFileFailed(Path file, IOException ioe)
268 throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800269 this.exception = ioe;
270 return FileVisitResult.TERMINATE;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800271 }
272 }
273
Madan Jampani30a57f82015-03-02 12:19:41 -0800274 /**
275 * Returns a human friendly time ago string for a specified system time.
Thomas Vachuska480adad2015-03-06 10:27:09 -0800276 *
Madan Jampani30a57f82015-03-02 12:19:41 -0800277 * @param unixTime system time in millis
278 * @return human friendly time ago
279 */
280 public static String timeAgo(long unixTime) {
281 long deltaMillis = System.currentTimeMillis() - unixTime;
282 long secondsSince = (long) (deltaMillis / 1000.0);
283 long minsSince = (long) (deltaMillis / (1000.0 * 60));
284 long hoursSince = (long) (deltaMillis / (1000.0 * 60 * 60));
285 long daysSince = (long) (deltaMillis / (1000.0 * 60 * 60 * 24));
286 if (daysSince > 0) {
287 return String.format("%dd ago", daysSince);
288 } else if (hoursSince > 0) {
289 return String.format("%dh ago", hoursSince);
290 } else if (minsSince > 0) {
291 return String.format("%dm ago", minsSince);
292 } else if (secondsSince > 0) {
293 return String.format("%ds ago", secondsSince);
294 } else {
295 return "just now";
296 }
297 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800298
299 /**
300 * Copies the specified directory path.&nbsp;Use with great caution since
301 * no attempt is made to check for symbolic links, which could result in
302 * copy of unintended files.
303 *
304 * @param src directory to be copied
305 * @param dst destination directory to be removed
306 * @throws java.io.IOException if unable to remove contents
307 */
308 public static void copyDirectory(String src, String dst) throws IOException {
309 walkFileTree(Paths.get(src), new DirectoryCopier(src, dst));
310 }
311
312 /**
313 * Copies the specified directory path.&nbsp;Use with great caution since
314 * no attempt is made to check for symbolic links, which could result in
315 * copy of unintended files.
316 *
317 * @param src directory to be copied
318 * @param dst destination directory to be removed
319 * @throws java.io.IOException if unable to remove contents
320 */
321 public static void copyDirectory(File src, File dst) throws IOException {
322 walkFileTree(Paths.get(src.getAbsolutePath()),
323 new DirectoryCopier(src.getAbsolutePath(),
324 dst.getAbsolutePath()));
325 }
326
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800327 // Auxiliary path visitor for recursive directory structure copying.
328 private static class DirectoryCopier extends SimpleFileVisitor<Path> {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800329 private Path src;
330 private Path dst;
331 private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING;
332
333 DirectoryCopier(String src, String dst) {
334 this.src = Paths.get(src);
335 this.dst = Paths.get(dst);
336 }
337
338 @Override
339 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
340 Path targetPath = dst.resolve(src.relativize(dir));
341 if (!Files.exists(targetPath)) {
342 Files.createDirectory(targetPath);
343 }
344 return FileVisitResult.CONTINUE;
345 }
346
347 @Override
348 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
349 Files.copy(file, dst.resolve(src.relativize(file)), copyOption);
350 return FileVisitResult.CONTINUE;
351 }
352 }
353
tom5f38b3a2014-08-27 23:50:54 -0700354}