/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.leansoft.luxun.utils; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.io.UnsupportedEncodingException; import java.lang.management.ManagementFactory; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.zip.CRC32; import javax.management.MBeanServer; import javax.management.ObjectName; import com.leansoft.luxun.mx.IMBeanName; /** * common utilities * */ public class Utils { /** * loading Properties from files * @param filename file path * @return properties * @throws RuntimeException while file not exist or loading fail */ public static Properties loadProps(String filename) { Properties props = new Properties(); FileInputStream fis = null; try { fis = new FileInputStream(filename); props.load(fis); return props; } catch (IOException ex) { throw new RuntimeException(ex); } finally { Closer.closeQuietly(fis); } } /** * Get a property of type java.util.Properties or return the default if * no such property is defined */ public static Properties getProps(Properties props, String name, Properties defaultProperties) { final String propString = props.getProperty(name); if (propString == null) return defaultProperties; String[] propValues = propString.split(","); if (propValues.length < 1) { throw new IllegalArgumentException("Illegal format of specifying properties '" + propString + "'"); } Properties properties = new Properties(); for (int i = 0; i < propValues.length; i++) { String[] prop = propValues[i].split("="); if (prop.length != 2) throw new IllegalArgumentException("Illegal format of specifying properties '" + propValues[i] + "'"); properties.put(prop[0], prop[1]); } return properties; } /** * Get a string property, or, if no such property is defined, return * the given default value * * @param props * @param name * @param defaultValue * @return value in the props or defaultValue while name not exist */ public static String getString(Properties props, String name, String defaultValue) { return props.containsKey(name) ? props.getProperty(name) : defaultValue; } public static String getString(Properties props, String name) { if (props.containsKey(name)) { return props.getProperty(name); } throw new IllegalArgumentException("Missing required property '" + name + "'"); } public static int getInt(Properties props, String name) { if (props.containsKey(name)) { return getInt(props, name, -1); } throw new IllegalArgumentException("Missing required property '" + name + "'"); } public static long getLong(Properties props, String name, long defaultValue) { return getLongInRange(props, name, defaultValue, Long.MIN_VALUE, Long.MAX_VALUE); } public static long getLongInRange(Properties props, String name, long defaultValue, long min, long max) { long v = defaultValue; if (props.containsKey(name)) { v = Long.valueOf(props.getProperty(name)); } if (v >= min && v <= max) { return v; } throw new IllegalArgumentException(name + " has value " + v + " which is not in the range"); } public static int getInt(Properties props, String name, int defaultValue) { return getIntInRange(props, name, defaultValue, Integer.MIN_VALUE, Integer.MAX_VALUE); } public static int getIntInRange(Properties props, String name, int defaultValue, int min, int max) { int v = defaultValue; if (props.containsKey(name)) { v = Integer.valueOf(props.getProperty(name)); } if (v >= min && v <= max) { return v; } throw new IllegalArgumentException(name + " has value " + v + " which is not in the range"); } public static boolean getBoolean(Properties props, String name, boolean defaultValue) { if (!props.containsKey(name)) return defaultValue; return "true".equalsIgnoreCase(props.getProperty(name)); } private static Map<String, Integer> getCSVMap(String value, String exceptionMsg, String successMsg) { Map<String, Integer> map = new LinkedHashMap<String, Integer>(); if (value == null || value.trim().length() < 3) return map; for (String one : value.trim().split(",")) { String[] kv = one.split(":"); //FIXME: force positive number map.put(kv[0].trim(), Integer.valueOf(kv[1].trim())); } return map; } public static Map<String, Integer> getTopicRentionHours(String retentionHours) { String exceptionMsg = "Malformed token for topic.log.retention.hours in server.properties: "; String successMsg = "The retention hour for "; return getCSVMap(retentionHours, exceptionMsg, successMsg); } public static Map<String, Integer> getTopicFlushIntervals(String allIntervals) { String exceptionMsg = "Malformed token for topic.flush.Intervals.ms in server.properties: "; String successMsg = "The flush interval for "; return getCSVMap(allIntervals, exceptionMsg, successMsg); } public static KV<String, Integer> getTopicPartition(String topicPartition) { int index = topicPartition.lastIndexOf('-'); return new KV<String, Integer>(topicPartition.substring(0, index),// Integer.valueOf(topicPartition.substring(index + 1))); } public static Map<String, Integer> getTopicPartitions(String allPartitions) { String exceptionMsg = "Malformed token for topic.partition.counts in server.properties: "; String successMsg = "The number of partitions for topic "; return getCSVMap(allPartitions, exceptionMsg, successMsg); } public static Map<String, Integer> getConsumerTopicMap(String consumerTopicString) { String exceptionMsg = "Malformed token for embeddedconsumer.topics in consumer.properties: "; String successMsg = "The number of consumer thread for topic "; return getCSVMap(consumerTopicString, exceptionMsg, successMsg); } /** * read data from channel to buffer * @param channel readable channel * @param buffer bytebuffer * @return read size * @throws IOException */ public static int read(ReadableByteChannel channel, ByteBuffer buffer) throws IOException { int count = channel.read(buffer); if (count == -1) throw new EOFException("Received -1 when reading from channel, socket has likely been closed."); return count; } /** * Write a size prefixed string where the size is stored as a 2 byte * short * * @param buffer The buffer to write to * @param s The string to write */ public static void writeShortString(ByteBuffer buffer, String s) { if (s == null) { buffer.putShort((short) -1); } else if (s.length() > Short.MAX_VALUE) { throw new IllegalArgumentException("String exceeds the maximum size of " + Short.MAX_VALUE + "."); } else { byte[] data = getBytes(s); //topic support non-ascii character buffer.putShort((short) data.length); buffer.put(data); } } public static String fromBytes(byte[] b) { return fromBytes(b, "UTF-8"); } public static String fromBytes(byte[] b, String encoding) { if(b == null) return null; try { return new String(b, encoding); } catch (UnsupportedEncodingException e) { return new String(b); } } public static byte[] getBytes(String s) { return getBytes(s, "UTF-8"); } public static byte[] getBytes(String s, String encoding) { if(s == null)return null; try { return s.getBytes(encoding); } catch (UnsupportedEncodingException e) { return s.getBytes(); } } /** * Read an unsigned integer from the current position in the buffer, * incrementing the position by 4 bytes * * @param buffer The buffer to read from * @return The integer read, as a long to avoid signedness */ public static long getUnsignedInt(ByteBuffer buffer) { return buffer.getInt() & 0xffffffffL; } /** * Read an unsigned integer from the given position without modifying * the buffers position * * @param buffer The buffer to read from * @param index the index from which to read the integer * @return The integer read, as a long to avoid signedness */ public static long getUnsignedInt(ByteBuffer buffer, int index) { return buffer.getInt(index) & 0xffffffffL; } /** * Write the given long value as a 4 byte unsigned integer. Overflow is * ignored. * * @param buffer The buffer to write to * @param value The value to write */ public static void putUnsignedInt(ByteBuffer buffer, long value) { buffer.putInt((int) (value & 0xffffffffL)); } /** * Write the given long value as a 4 byte unsigned integer. Overflow is * ignored. * * @param buffer The buffer to write to * @param index The position in the buffer at which to begin writing * @param value The value to write */ public static void putUnsignedInt(ByteBuffer buffer, int index, long value) { buffer.putInt(index, (int) (value & 0xffffffffL)); } /** * Compute the CRC32 of the byte array * * @param bytes The array to compute the checksum for * @return The CRC32 */ public static long crc32(byte[] bytes) { return crc32(bytes, 0, bytes.length); } /** * Compute the CRC32 of the segment of the byte array given by the * specificed size and offset * * @param bytes The bytes to checksum * @param offset the offset at which to begin checksumming * @param size the number of bytes to checksum * @return The CRC32 */ public static long crc32(byte[] bytes, int offset, int size) { CRC32 crc = new CRC32(); crc.update(bytes, offset, size); return crc.getValue(); } /** * Create a new thread * * @param name The name of the thread * @param runnable The work for the thread to do * @param daemon Should the thread block JVM shutdown? * @return The unstarted thread */ public static Thread newThread(String name, Runnable runnable, boolean daemon) { Thread thread = new Thread(runnable, name); thread.setDaemon(daemon); return thread; } /** * read bytes with a short sign prefix(mark the size of bytes) * * @param buffer data buffer * @return string result(encoding with UTF-8) * @see #writeShortString(ByteBuffer, String) */ public static String readShortString(ByteBuffer buffer) { short size = buffer.getShort(); if (size < 0) { return null; } byte[] bytes = new byte[size]; buffer.get(bytes); return fromBytes(bytes); } /** * caculate string length with size prefix * * @param topic the string value (support UTF-8 bytes) * @return string size with short prefix (2+topic.length) * @see #readShortString(ByteBuffer) */ public static int caculateShortString(String topic) { return 2 + getBytes(topic).length; } public static boolean registerMBean(IMBeanName object) { return registerMBean(object, object.getMbeanName()); } /** * Register the given mbean with the platform mbean server, * unregistering any mbean that was there before. Note, this method * will not throw an exception if the registration fails (since there * is nothing you can do and it isn't fatal), instead it just returns * false indicating the registration failed. * * @param mbean The object to register as an mbean * @param name The name to register this mbean with * @returns true if the registration succeeded */ static boolean registerMBean(Object mbean, String name) { try { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); synchronized (mbs) { ObjectName objName = new ObjectName(name); if (mbs.isRegistered(objName)) { mbs.unregisterMBean(objName); } mbs.registerMBean(mbean, objName); } return true; } catch (Exception e) { e.printStackTrace(); } return false; } public static void unregisterMBean(IMBeanName mbean) { unregisterMBean(mbean.getMbeanName()); } /** * Unregister the mbean with the given name, if there is one registered * * @param name The mbean name to unregister * @see #registerMBean(Object, String) */ private static void unregisterMBean(String name) { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); try { synchronized (mbs) { ObjectName objName = new ObjectName(name); if (mbs.isRegistered(objName)) { mbs.unregisterMBean(objName); } } } catch (Exception e) { e.printStackTrace(); } } /** * open a readable or writeable FileChannel * @param file file object * @param mutable writeable * @return open the FileChannel */ @SuppressWarnings("resource") public static FileChannel openChannel(File file, boolean mutable) throws IOException { if (mutable) { return new RandomAccessFile(file, "rw").getChannel(); } return new FileInputStream(file).getChannel(); } public static List<String> getCSVList(String csvList) { if (csvList == null || csvList.length() == 0) return Collections.emptyList(); List<String> ret = new ArrayList<String>(Arrays.asList(csvList.split(","))); Iterator<String> iter = ret.iterator(); while (iter.hasNext()) { final String next = iter.next(); if (next == null || next.length() == 0) { iter.remove(); } } return ret; } /** * create an instance from the className * @param className full class name * @return an object or null if className is null */ @SuppressWarnings("unchecked") public static <E> E getObject(String className) { if (className == null) { return (E) null; } try { return (E) Class.forName(className).newInstance(); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } public static String toString(ByteBuffer buffer, String encoding) { byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); return fromBytes(bytes, encoding); } public static File getCanonicalFile(File f) { try { return f.getCanonicalFile(); } catch (IOException e) { return f.getAbsoluteFile(); } } public static ByteBuffer serializeArray(long[] numbers) { int size = 4 + 8 * numbers.length; ByteBuffer buffer = ByteBuffer.allocate(size); buffer.putInt(numbers.length); for(long num: numbers) { buffer.putLong(num); } buffer.rewind(); return buffer; } public static ByteBuffer serializeArray(int[] numbers) { int size = 4 + 4 * numbers.length; ByteBuffer buffer = ByteBuffer.allocate(size); buffer.putInt(numbers.length); for(int num: numbers) { buffer.putInt(num); } buffer.rewind(); return buffer; } public static int[] deserializeIntArray(ByteBuffer buffer) { int size = buffer.getInt(); int[] nums = new int[size]; for (int i = 0; i < size; i++) { nums[i] = buffer.getInt(); } return nums; } public static long[] deserializeLongArray(ByteBuffer buffer) { int size = buffer.getInt(); long[] nums = new long[size]; for (int i = 0; i < size; i++) { nums[i] = buffer.getLong(); } return nums; } private final static char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * digest message with MD5 * @param source message * @return 32 bit MD5 value (lower case) */ public static String md5(byte[] source) { try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(source); byte tmp[] = md.digest(); char str[] = new char[32]; int k = 0; for (byte b : tmp) { str[k++] = hexDigits[b >>> 4 & 0xf]; str[k++] = hexDigits[b & 0xf]; } return new String(str); } catch (Exception e) { throw new IllegalArgumentException(e); } } public static void deleteDirectory(String dir) { deleteDirectory(new File(dir)); } public static void deleteDirectory(File dir) { if (!dir.exists()) return; File[] subs = dir.listFiles(); if (subs != null) { for (File f : dir.listFiles()) { if (f.isFile()) { if(!f.delete()) { throw new IllegalStateException("delete file failed: "+f); } } else { deleteDirectory(f); } } } if(!dir.delete()) { throw new IllegalStateException("delete directory failed: "+dir); } } // Empty checks //----------------------------------------------------------------------- /** * <p>Checks if a String is empty ("") or null.</p> * * <pre> * StringUtils.isEmpty(null) = true * StringUtils.isEmpty("") = true * StringUtils.isEmpty(" ") = false * StringUtils.isEmpty("bob") = false * StringUtils.isEmpty(" bob ") = false * </pre> * * <p>NOTE: This method changed in Lang version 2.0. * It no longer trims the String. * That functionality is available in isBlank().</p> * * @param str the String to check, may be null * @return <code>true</code> if the String is empty or null */ public static boolean isStringEmpty(String str) { return str == null || str.length() == 0; } }