blob: e3a3a41ade079c66a2ca11c963965243b87462c4 [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;
tom53efab52014-10-07 17:43:48 -070038import java.util.List;
tom5f38b3a2014-08-27 23:50:54 -070039import java.util.concurrent.ThreadFactory;
40
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080041import static java.nio.file.Files.delete;
42import static java.nio.file.Files.walkFileTree;
43import static org.onlab.util.GroupedThreadFactory.groupedThreadFactory;
44import static org.slf4j.LoggerFactory.getLogger;
Ray Milkey705d9bc2014-11-18 08:19:00 -080045
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080046/**
47 * Miscellaneous utility methods.
48 */
tom5f38b3a2014-08-27 23:50:54 -070049public abstract class Tools {
50
51 private Tools() {
52 }
53
Thomas Vachuska02aeb032015-01-06 22:36:30 -080054 private static final Logger log = getLogger(Tools.class);
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080055
tom5f38b3a2014-08-27 23:50:54 -070056 /**
57 * Returns a thread factory that produces threads named according to the
58 * supplied name pattern.
59 *
60 * @param pattern name pattern
61 * @return thread factory
62 */
63 public static ThreadFactory namedThreads(String pattern) {
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080064 return new ThreadFactoryBuilder()
65 .setNameFormat(pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080066 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
67 .build();
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080068 }
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080069
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080070 /**
71 * Returns a thread factory that produces threads named according to the
72 * supplied name pattern and from the specified thread-group. The thread
73 * group name is expected to be specified in slash-delimited format, e.g.
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080074 * {@code onos/intent}. The thread names will be produced by converting
75 * the thread group name into dash-delimited format and pre-pended to the
76 * specified pattern.
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080077 *
78 * @param groupName group name in slash-delimited format to indicate hierarchy
79 * @param pattern name pattern
80 * @return thread factory
81 */
82 public static ThreadFactory groupedThreads(String groupName, String pattern) {
83 return new ThreadFactoryBuilder()
84 .setThreadFactory(groupedThreadFactory(groupName))
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080085 .setNameFormat(groupName.replace(GroupedThreadFactory.DELIMITER, "-") + "-" + pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080086 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
87 .build();
tom5f38b3a2014-08-27 23:50:54 -070088 }
89
tom782a7cf2014-09-11 23:58:38 -070090 /**
Yuta HIGUCHI06586272014-11-25 14:27:03 -080091 * Returns a thread factory that produces threads with MIN_PRIORITY.
92 *
93 * @param factory backing ThreadFactory
94 * @return thread factory
95 */
96 public static ThreadFactory minPriority(ThreadFactory factory) {
97 return new ThreadFactoryBuilder()
Thomas Vachuska02aeb032015-01-06 22:36:30 -080098 .setThreadFactory(factory)
99 .setPriority(Thread.MIN_PRIORITY)
100 .build();
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800101 }
102
103 /**
Brian O'Connore2eac102015-02-12 18:30:22 -0800104 * Returns true if the collection is null or is empty.
105 *
106 * @param collection collection to test
107 * @return true if null or empty; false otherwise
108 */
109 public static boolean isNullOrEmpty(Collection collection) {
110 return collection == null || collection.isEmpty();
111 }
112
113 /**
tom782a7cf2014-09-11 23:58:38 -0700114 * Converts a string from hex to long.
115 *
116 * @param string hex number in string form; sans 0x
117 * @return long value
118 */
119 public static long fromHex(String string) {
120 return UnsignedLongs.parseUnsignedLong(string, 16);
121 }
122
123 /**
124 * Converts a long value to hex string; 16 wide and sans 0x.
125 *
126 * @param value long value
127 * @return hex string
128 */
129 public static String toHex(long value) {
130 return Strings.padStart(UnsignedLongs.toString(value, 16), 16, '0');
131 }
132
133 /**
134 * Converts a long value to hex string; 16 wide and sans 0x.
135 *
136 * @param value long value
137 * @param width string width; zero padded
138 * @return hex string
139 */
140 public static String toHex(long value, int width) {
141 return Strings.padStart(UnsignedLongs.toString(value, 16), width, '0');
142 }
tomf110fff2014-09-26 00:38:18 -0700143
144 /**
145 * Suspends the current thread for a specified number of millis.
146 *
147 * @param ms number of millis
148 */
149 public static void delay(int ms) {
150 try {
151 Thread.sleep(ms);
152 } catch (InterruptedException e) {
153 throw new RuntimeException("Interrupted", e);
154 }
155 }
156
tom53efab52014-10-07 17:43:48 -0700157 /**
158 * Slurps the contents of a file into a list of strings, one per line.
159 *
160 * @param path file path
161 * @return file contents
162 */
163 public static List<String> slurp(File path) {
Ray Milkey705d9bc2014-11-18 08:19:00 -0800164 try {
165 BufferedReader br = new BufferedReader(
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800166 new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8));
Ray Milkey705d9bc2014-11-18 08:19:00 -0800167
tom53efab52014-10-07 17:43:48 -0700168 List<String> lines = new ArrayList<>();
169 String line;
170 while ((line = br.readLine()) != null) {
171 lines.add(line);
172 }
173 return lines;
174
175 } catch (IOException e) {
176 return null;
177 }
178 }
179
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800180
181 /**
182 * Purges the specified directory path.&nbsp;Use with great caution since
183 * no attempt is made to check for symbolic links, which could result in
184 * deletion of unintended files.
185 *
186 * @param path directory to be removed
187 * @throws java.io.IOException if unable to remove contents
188 */
189 public static void removeDirectory(String path) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800190 DirectoryDeleter visitor = new DirectoryDeleter();
191 walkFileTree(Paths.get(path), visitor);
192 if (visitor.exception != null) {
193 throw visitor.exception;
194 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800195 }
196
197 /**
198 * Purges the specified directory path.&nbsp;Use with great caution since
199 * no attempt is made to check for symbolic links, which could result in
200 * deletion of unintended files.
201 *
202 * @param dir directory to be removed
203 * @throws java.io.IOException if unable to remove contents
204 */
205 public static void removeDirectory(File dir) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800206 DirectoryDeleter visitor = new DirectoryDeleter();
207 walkFileTree(Paths.get(dir.getAbsolutePath()), visitor);
208 if (visitor.exception != null) {
209 throw visitor.exception;
210 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800211 }
212
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800213 // Auxiliary path visitor for recursive directory structure removal.
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800214 private static class DirectoryDeleter extends SimpleFileVisitor<Path> {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800215
216 private IOException exception;
217
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800218 @Override
219 public FileVisitResult visitFile(Path file, BasicFileAttributes attributes)
220 throws IOException {
221 if (attributes.isRegularFile()) {
222 delete(file);
223 }
224 return FileVisitResult.CONTINUE;
225 }
226
227 @Override
228 public FileVisitResult postVisitDirectory(Path directory, IOException ioe)
229 throws IOException {
230 delete(directory);
231 return FileVisitResult.CONTINUE;
232 }
233
234 @Override
235 public FileVisitResult visitFileFailed(Path file, IOException ioe)
236 throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800237 this.exception = ioe;
238 return FileVisitResult.TERMINATE;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800239 }
240 }
241
Madan Jampani30a57f82015-03-02 12:19:41 -0800242 /**
243 * Returns a human friendly time ago string for a specified system time.
Thomas Vachuska480adad2015-03-06 10:27:09 -0800244 *
Madan Jampani30a57f82015-03-02 12:19:41 -0800245 * @param unixTime system time in millis
246 * @return human friendly time ago
247 */
248 public static String timeAgo(long unixTime) {
249 long deltaMillis = System.currentTimeMillis() - unixTime;
250 long secondsSince = (long) (deltaMillis / 1000.0);
251 long minsSince = (long) (deltaMillis / (1000.0 * 60));
252 long hoursSince = (long) (deltaMillis / (1000.0 * 60 * 60));
253 long daysSince = (long) (deltaMillis / (1000.0 * 60 * 60 * 24));
254 if (daysSince > 0) {
255 return String.format("%dd ago", daysSince);
256 } else if (hoursSince > 0) {
257 return String.format("%dh ago", hoursSince);
258 } else if (minsSince > 0) {
259 return String.format("%dm ago", minsSince);
260 } else if (secondsSince > 0) {
261 return String.format("%ds ago", secondsSince);
262 } else {
263 return "just now";
264 }
265 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800266
267 /**
268 * Copies the specified directory path.&nbsp;Use with great caution since
269 * no attempt is made to check for symbolic links, which could result in
270 * copy of unintended files.
271 *
272 * @param src directory to be copied
273 * @param dst destination directory to be removed
274 * @throws java.io.IOException if unable to remove contents
275 */
276 public static void copyDirectory(String src, String dst) throws IOException {
277 walkFileTree(Paths.get(src), new DirectoryCopier(src, dst));
278 }
279
280 /**
281 * Copies the specified directory path.&nbsp;Use with great caution since
282 * no attempt is made to check for symbolic links, which could result in
283 * copy of unintended files.
284 *
285 * @param src directory to be copied
286 * @param dst destination directory to be removed
287 * @throws java.io.IOException if unable to remove contents
288 */
289 public static void copyDirectory(File src, File dst) throws IOException {
290 walkFileTree(Paths.get(src.getAbsolutePath()),
291 new DirectoryCopier(src.getAbsolutePath(),
292 dst.getAbsolutePath()));
293 }
294
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800295 // Auxiliary path visitor for recursive directory structure copying.
296 private static class DirectoryCopier extends SimpleFileVisitor<Path> {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800297 private Path src;
298 private Path dst;
299 private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING;
300
301 DirectoryCopier(String src, String dst) {
302 this.src = Paths.get(src);
303 this.dst = Paths.get(dst);
304 }
305
306 @Override
307 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
308 Path targetPath = dst.resolve(src.relativize(dir));
309 if (!Files.exists(targetPath)) {
310 Files.createDirectory(targetPath);
311 }
312 return FileVisitResult.CONTINUE;
313 }
314
315 @Override
316 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
317 Files.copy(file, dst.resolve(src.relativize(file)), copyOption);
318 return FileVisitResult.CONTINUE;
319 }
320 }
321
tom5f38b3a2014-08-27 23:50:54 -0700322}