package org.radargun.utils; import java.io.*; import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryUsage; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.NetworkInterface; import java.net.SocketException; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.PosixFileAttributeView; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import javax.management.MBeanServer; import com.sun.management.HotSpotDiagnosticMXBean; import org.radargun.Directories; import org.radargun.config.InitHelper; import org.radargun.config.PropertyHelper; import org.radargun.logging.Log; import org.radargun.logging.LogFactory; /** * @author Mircea Markus <Mircea.Markus@jboss.com> */ public class Utils { private static Log log = LogFactory.getLog(Utils.class); private static final String PROPERTIES_FILE = "plugin.properties"; private static final NumberFormat NF = new DecimalFormat("##,###"); private static final NumberFormat MEM_FMT = new DecimalFormat("##,###.##"); private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic"; private static final String HOTSPOT_BEAN_CLASS = "com.sun.management.HotSpotDiagnosticMXBean"; private static final String HOTSPOT_BEAN_DUMP_METHOD = "dumpHeap"; // field to store the hotspot diagnostic MBean private static volatile Object hotspotMBean; // field to store random seed private static volatile Field randomSeedField = null; private Utils() { } public static String getMillisDurationString(long millis) { long secs = millis / 1000; long mins = secs / 60; long remainingSecs = secs % 60; if (mins > 0) { return String.format("%d mins %d secs", mins, remainingSecs); } else { return String.format("%.3f secs", millis / 1000.0); } } public static String getNanosDurationString(long nanos) { return getMillisDurationString(nanos / 1000000); } public static String getMemoryInfo() { Runtime run = Runtime.getRuntime(); StringBuilder memoryInfo = new StringBuilder(); memoryInfo.append("Runtime free: ").append(kbString(run.freeMemory())) .append("\nRuntime max:").append(kbString(run.maxMemory())) .append("\nRuntime total:").append(kbString(run.totalMemory())); Iterator<MemoryPoolMXBean> iter = ManagementFactory.getMemoryPoolMXBeans().iterator(); while (iter.hasNext()) { MemoryPoolMXBean item = iter.next(); MemoryUsage usage = item.getUsage(); memoryInfo.append("\nMX ").append(item.getName()).append("(").append(item.getType()).append("): ") .append("used: ").append(kbString(usage.getUsed())) .append(", init: ").append(kbString(usage.getInit())) .append(", committed: ").append(kbString(usage.getCommitted())) .append(", max: ").append(kbString(usage.getMax())); } return memoryInfo.toString(); } private static String format(long bytes) { double val = bytes; int mag = 0; while (val > 1024) { val = val / 1024; mag++; } String formatted = MEM_FMT.format(val); switch (mag) { case 0: return formatted + " bytes"; case 1: return formatted + " kb"; case 2: return formatted + " Mb"; case 3: return formatted + " Gb"; default: return "WTF?"; } } public static String kbString(long memBytes) { return MEM_FMT.format(memBytes / 1024) + " kb"; } public static void threadDump() { long start = TimeService.nanoTime(); Map<Thread, StackTraceElement[]> stacktraces = Thread.getAllStackTraces(); long duration = TimeService.nanoTime() - start; log.warn("Thread dump took " + TimeUnit.NANOSECONDS.toMillis(duration) + " ms:"); for (Entry<Thread, StackTraceElement[]> st : stacktraces.entrySet()) { StringBuilder sb = new StringBuilder(); sb.append("Stack for thread "); sb.append(st.getKey().getName()); sb.append(" ("); sb.append(st.getKey().getState()); sb.append("):\n"); for (StackTraceElement ste : st.getValue()) { sb.append(ste.toString()); sb.append('\n'); } log.warn(sb.toString()); } } public static byte[] readAsBytes(InputStream is) throws IOException { try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) { int nRead; byte[] data = new byte[16384]; while ((nRead = is.read(data, 0, data.length)) != -1) { buffer.write(data, 0, nRead); } buffer.flush(); return buffer.toByteArray(); } } public static String readAsString(InputStream is) throws IOException { return new Scanner(is, "UTF-8").useDelimiter("\\A").next(); } public static String sanitizePath(String pluginPath) { if (pluginPath.startsWith("~/")) { pluginPath = System.getProperty("user.home") + File.separator + pluginPath.substring(2); } return pluginPath; } public static String concat(String separator, String... parts) { StringBuilder sb = new StringBuilder(); for (String part : parts) { if (part == null || part.isEmpty()) continue; if (sb.length() != 0) sb.append(separator); sb.append(part); } return sb.toString(); } public static void shutdownAndWait(ExecutorService executor) { executor.shutdownNow(); try { if (!executor.awaitTermination(10, TimeUnit.SECONDS)) { log.error("Failed to terminate executor."); } } catch (InterruptedException e) { log.error("Interrupted when waiting for the executor to finish."); } } public static void deleteOnExitRecursive(File file) { if (!file.exists()) return; file.deleteOnExit(); if (!file.isDirectory()) return; for (File f : file.listFiles()) deleteOnExitRecursive(f); } public static String getCodePath(Class<?> clazz) { return clazz.getProtectionDomain().getCodeSource().getLocation().getPath(); } /** * Note that this relies on unspecified JVM behaviour * @return Process ID or 0 if this cannot be found */ public static int getProcessID() { String processName = ManagementFactory.getRuntimeMXBean().getName(); int atIndex = processName.indexOf('@'); if (atIndex >= 0) { processName = processName.substring(0, atIndex); } try { return Integer.parseInt(processName); } catch (NumberFormatException e) { log.warn("Could not get ID of current process", e); } return 0; } public static SlaveConnectionInfo getSlaveConnectionInfo(int slaveIndex) throws SocketException { Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces(); SlaveConnectionInfo connection = new SlaveConnectionInfo(); for (NetworkInterface netint : Collections.list(nets)) { connection.addAddresses(slaveIndex, netint.getName(), Collections.list(netint.getInetAddresses())); Collections.list(netint.getInetAddresses()).stream().forEach(x -> System.out.println(x.getHostAddress())); } return connection; } public static class JarFilenameFilter implements FilenameFilter { public boolean accept(File dir, String name) { String fileName = name.toUpperCase(Locale.ENGLISH); if (fileName.endsWith("JAR") || fileName.endsWith("ZIP")) { if (log.isTraceEnabled()) { log.trace("Accepting file: " + fileName); } return true; } else { if (log.isTraceEnabled()) { log.trace("Rejecting file: " + fileName); } return false; } } } public static Properties getPluginProperties(String plugin) { ArgsHolder.PluginParam pluginParam = ArgsHolder.getPluginParams().get(plugin); Properties properties = new Properties(); File pluginDir = pluginParam != null && pluginParam.getPath() != null ? new File(pluginParam.getPath()) : new File(Directories.PLUGINS_DIR, plugin); File confDir = new File(pluginDir, "conf"); File file = new File(confDir, PROPERTIES_FILE); if (!file.exists()) { log.warn("Could not find a plugin descriptor : " + file); return properties; } try { loadProperties(properties, file); } catch (IOException e) { log.error(String.format("Exception while loading plugin specific properties file %s.", PROPERTIES_FILE), e); } return properties; } public static Properties getJarProperties(File file) throws IOException { JarFile jarFile = new JarFile(file); JarEntry entry = jarFile.getJarEntry(PROPERTIES_FILE); if (entry == null) { return null; } try (InputStream stream = jarFile.getInputStream(entry)) { Properties properties = new Properties(); loadProperties(properties, stream); return properties; } } public static Properties loadProperties(Properties properties, File file) throws IOException { try (InputStream propertiesStream = new FileInputStream(file)) { return loadProperties(properties, propertiesStream); } } public static Properties loadProperties(Properties properties, InputStream inputStream) throws IOException { properties.load(inputStream); return properties; } public static String getPluginProperty(String plugin, String propertyName) { return getPluginProperties(plugin).getProperty(propertyName); } public static File createOrReplaceFile(File parentDir, String actualFileName) throws IOException { File outputFile = new File(parentDir, actualFileName); backupFile(outputFile); if (outputFile.createNewFile()) { log.info("Successfully created report file:" + outputFile.getAbsolutePath()); } else { log.warn("Failed to create the report file!"); } return outputFile; } public static void backupFile(File outputFile) { if (outputFile.exists()) { int lastIndexOfDot = outputFile.getName().lastIndexOf('.'); String extension = lastIndexOfDot > 0 ? outputFile.getName().substring(lastIndexOfDot) : ""; File old = new File(outputFile.getParentFile(), "old"); if (!old.exists()) { if (old.mkdirs()) { log.warn("Issues whilst creating dir: " + old); } } String fileName = outputFile.getName() + ".old." + TimeService.currentTimeMillis() + extension; File newFile = new File(old, fileName); log.info("A file named: '" + outputFile.getAbsolutePath() + "' already exists. Moving it to '" + newFile + "'"); if (!outputFile.renameTo(newFile)) { log.warn("Could not rename!!!"); } } } public static void deleteDirectory(File file) { if (file.isDirectory()) { for (File f : file.listFiles()) { deleteDirectory(f); } } if (file.exists()) { file.delete(); } } public static String prettyPrintTime(long time, TimeUnit unit) { NumberFormat nf = NumberFormat.getNumberInstance(); nf.setMaximumFractionDigits(2); if (unit.toNanos(time) < 1000) return nf.format(convert(time, unit, TimeUnit.NANOSECONDS)) + " ns"; if (unit.toMicros(time) < 1000) return nf.format(convert(time, unit, TimeUnit.MICROSECONDS)) + " us"; if (unit.toMillis(time) < 1000) return nf.format(convert(time, unit, TimeUnit.MILLISECONDS)) + " ms"; if (unit.toSeconds(time) < 300) return nf.format(convert(time, unit, TimeUnit.SECONDS)) + " seconds"; if (unit.toMinutes(time) < 120) return nf.format(convert(time, unit, TimeUnit.MINUTES)) + " minutes"; return nf.format(convert(time, unit, TimeUnit.HOURS)) + " hours"; } private static double convert(long time, TimeUnit unitIn, TimeUnit unitOut) { return (double) unitIn.toNanos(time) / (double) unitOut.toNanos(1); } /** * Prints a time for display * * @param millis time in millis * @return the time, represented as millis, seconds, minutes or hours as appropriate, with suffix */ public static String prettyPrintMillis(long millis) { return prettyPrintTime(millis, TimeUnit.MILLISECONDS); } public static void sleep(long duration) { try { Thread.sleep(duration); } catch (InterruptedException e) { throw new IllegalStateException(e); } } public static String numberFormat(int i) { return NF.format(i); } public static String numberFormat(double d) { return NF.format(d); } public static <T> T instantiate(String className) { try { return (T) Class.forName(className).newInstance(); } catch (Exception e) { throw new IllegalArgumentException(e); } } public static <T> T instantiateAndInit(String className, String params) { T instance = instantiate(className); if (params != null) { PropertyHelper.setProperties(instance, parseParams(params), false, false); } InitHelper.init(instance); return instance; } public static long string2Millis(String duration) { long durationMillis = 0; duration = duration.toUpperCase().trim(); int days = duration.indexOf('D'); int hours = duration.indexOf('H'); int minutes = duration.indexOf('M'); int seconds = duration.indexOf('S'); int lastIndex = 0; try { if (days > 0) { durationMillis += TimeUnit.DAYS.toMillis(Long.parseLong(duration.substring(lastIndex, days).trim())); lastIndex = days + 1; } if (hours > 0) { durationMillis += TimeUnit.HOURS.toMillis(Long.parseLong(duration.substring(lastIndex, hours).trim())); lastIndex = hours + 1; } if (minutes > 0) { durationMillis += TimeUnit.MINUTES.toMillis(Long.parseLong(duration.substring(lastIndex, minutes).trim())); lastIndex = minutes + 1; } if (seconds > 0) { durationMillis += TimeUnit.SECONDS.toMillis(Long.parseLong(duration.substring(lastIndex, seconds).trim())); lastIndex = seconds + 1; } if (lastIndex < duration.length()) { durationMillis += Long.parseLong(duration.substring(lastIndex)); } } catch (NumberFormatException nfe) { throw new IllegalArgumentException("Cannot parse string: '" + duration + "'", nfe); } return durationMillis; } @Deprecated public static void createOutputFile(String fileName, String fileContent) throws IOException { createOutputFile(fileName, fileContent, true); } @Deprecated public static void createOutputFile(String fileName, String fileContent, boolean doBackup) throws IOException { File parentDir = new File("reports"); if (!parentDir.exists() && !parentDir.mkdirs()) { log.error("Directory '" + parentDir.getAbsolutePath() + "' could not be created"); /* * The file will be created in the Sytem tmp directory */ parentDir = new File(System.getProperty("java.io.tmpdir")); } File reportFile = new File(parentDir, fileName); if (!reportFile.exists() || doBackup) { reportFile = Utils.createOrReplaceFile(parentDir, fileName); } if (!reportFile.exists()) { throw new IllegalStateException(reportFile.getAbsolutePath() + " was deleted? Not allowed to delete report file during test run!"); } else { log.info("Report file '" + reportFile.getAbsolutePath() + "' created"); } FileWriter writer = null; try { writer = new FileWriter(reportFile, !doBackup); writer.append(fileContent); } finally { if (writer != null) writer.close(); } } /** * Parse a string containing method names and String parameters. Multiple method names and parameters are separated * by a ';'. Method names and parameters are separated by a ':'. * * @return a Map with the method name as the key, and the String parameter as the value, or an empty Map if * <code>parameterString</code> is <code>null</code> */ public static Map<String, String> parseParams(String parameterString) { Map<String, String> result = new HashMap<String, String>(); if (parameterString != null) { for (String propAndValue : parameterString.split(";")) { String[] values = propAndValue.split(":"); result.put(values[0].trim(), values[1].trim()); } } return result; } /** * * Invoke a public method with a String argument on an Object * * @param object * the Object where the method is invoked * @param properties * a Collection of key-value pairs where the public method name is the key, and the String parameter is the value * @return the modified Object, or <code>null</code> if the field can't be changed */ public static <T> T invokeMethodWithProperties(T object, Collection<KeyValueProperty> properties) { Class<?> clazz = object.getClass(); if (properties != null) { for (KeyValueProperty property : properties) { try { Method method = clazz.getDeclaredMethod(property.getKey(), String.class); method.invoke(object, property.getValue()); } catch (Exception e) { log.error("Error invoking method named " + property.getKey() + " with value " + property.getValue(), e); return null; } } } return object; } public static void dumpHeap(String file) throws Exception { if (hotspotMBean == null) { synchronized (Utils.class) { if (hotspotMBean == null) { MBeanServer server = ManagementFactory.getPlatformMBeanServer(); hotspotMBean = ManagementFactory.newPlatformMXBeanProxy(server, HOTSPOT_BEAN_NAME, HotSpotDiagnosticMXBean.class); } } } Class clazz = Class.forName(HOTSPOT_BEAN_CLASS); Method m = clazz.getMethod(HOTSPOT_BEAN_DUMP_METHOD, String.class, boolean.class); m.invoke(hotspotMBean, file, true); } public static long getRandomSeed(Random random) { try { Field field = randomSeedField; if (field == null) { field = Random.class.getDeclaredField("seed"); field.setAccessible(true); randomSeedField = field; } return ((AtomicLong) field.get(random)).get(); } catch (Exception e) { log.error("Cannot access seed", e); throw new RuntimeException(e); } } public static Random setRandomSeed(Random random, long seed) { random.setSeed(seed ^ 0x5DEECE66DL); return random; } /** * Sort and save properties to a file. * * @param props Properties * @param file file * @throws Exception */ public static void saveSorted(Properties props, File file) throws Exception { try (FileOutputStream fout = new FileOutputStream(file)) { Properties sorted = new Properties() { @Override public Set<Object> keySet() { return Collections.unmodifiableSet(new TreeSet<Object>(super.keySet())); } @Override public synchronized Enumeration<Object> keys() { return Collections.enumeration(new TreeSet<Object>(super.keySet())); } @Override public Set<String> stringPropertyNames() { return Collections.unmodifiableSet(new TreeSet<String>(super.stringPropertyNames())); } }; sorted.putAll(props); sorted.storeToXML(fout, null); fout.flush(); } } public static List<String> readFile(String file) { List<String> lines = new ArrayList<String>(); BufferedReader br = null; InputStream resourceStream = Utils.class.getResourceAsStream(file); try { if (resourceStream != null) { br = new BufferedReader(new InputStreamReader(resourceStream)); } else { br = new BufferedReader(new FileReader(file)); } String line; while ((line = br.readLine()) != null) { lines.add(line); } } catch (IOException ex) { log.error("Error is thrown during file reading!", ex); throw new RuntimeException(ex); } finally { close(br, resourceStream); } return lines; } public static void close(Closeable... closeables) { if (closeables == null) { return; } IOException exception = null; for (Closeable closeable : closeables) { try { if (closeable != null) closeable.close(); } catch (IOException ex) { log.error("Error during closing!", ex); if (exception == null) exception = ex; } } if (exception != null) { throw new RuntimeException(exception); } } public static void setField(Class<?> clazz, String fieldName, Object instance, Object value) { try { Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); if (Modifier.isFinal(field.getModifiers())) { Field modifiers = Field.class.getDeclaredField("modifiers"); modifiers.setAccessible(true); modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); } field.set(instance, value); } catch (Exception e) { if (e instanceof NoSuchFieldException && clazz.getSuperclass() != null) { setField(clazz.getSuperclass(), fieldName, instance, value); } else { log.error("Failed to set " + clazz.getName() + "." + fieldName + " to value", e); } } } public static Object getField(Class<?> clazz, Object instance, String fieldName) throws Exception { Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); return field.get(instance); } public static <T> T getArg(Object[] args, int arg, Class<T> clazz) { if (args == null || args.length <= arg || !clazz.isInstance(args[arg])) throw new IllegalArgumentException(Arrays.toString(args)); return (T) args[arg]; } public static <T> T findThrowableCauseByClass(Throwable t, Class<T> clazz) { if (clazz == null) { throw new IllegalArgumentException("'clazz' parameter cannot be null."); } if (t == null) { return null; } else if (clazz.isAssignableFrom(t.getClass())) { return (T) t; } else { return findThrowableCauseByClass(t.getCause(), clazz); } } public static void loadConfigFile(String filename, Map<String, byte[]> configs) { if (filename != null) { InputStream is = null; try { is = Utils.class.getClassLoader().getResourceAsStream(filename); if (is == null) { //not attached as resource - assume the direct path on filesystem is = new FileInputStream(new File(filename)); } configs.put(filename, Utils.readAsBytes(is)); } catch (Exception e) { log.error("Error while reading configuration file (" + filename + ")", e); } finally { Utils.close(is); } } } /** * Unzip the zip file located at zipPath to directory targetDirectory. * Note that the extraction removes executable bits from files, so you need to set them afterwards, * for example using the {@link Utils#setPermissions(String, String)} method. */ public static void unzip(String zipPath, String targetDirectory) throws IOException { Path targetPath = FileSystems.getDefault().getPath(targetDirectory); Utils.deleteDirectory(targetPath.toFile()); Files.createDirectories(targetPath); ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipPath)); ZipEntry nextEntry; while ((nextEntry = zipInputStream.getNextEntry()) != null) { Path entryOutput = targetPath.resolve(nextEntry.getName()); if (nextEntry.isDirectory()) { Files.createDirectory(entryOutput); } else { Files.copy(zipInputStream, entryOutput); } zipInputStream.closeEntry(); } } /** * Sets the permissions (for example "rwxr-xr-x") on the file. */ public static void setPermissions(String file, String permissions) throws IOException { Path serverExecutable = FileSystems.getDefault().getPath(file); Set<PosixFilePermission> perms = PosixFilePermissions.fromString(permissions); if (Files.getFileAttributeView(serverExecutable, PosixFileAttributeView.class) != null) { Files.setPosixFilePermissions(serverExecutable, perms); } } }