public class IdWorker {
// worker编号位数
private static final long WORKER_BITS = 6L;
// worker编号最大值,决定支持的部署节点数量
private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_BITS);
// 毫秒内自增位数,每毫秒最大序号支持65535
private static final long SEQUENCE_BITS = 16L;
// worker编号偏移量
private static final long WORKER_SHIFT = SEQUENCE_BITS;
// 时间偏移量
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_BITS;
// 序号掩码
private static final long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS);
// 毫秒基线:2015-01-01 00:00:00
private static final long BASE_TIMESTAMP = 1420041600000L;
private final long workerId;
private volatile long sequence = 0L;
private volatile long lastTimestamp = -1L;
/**
* 从环境变量中获取worker编号,每个部署环境编号不能重复
*/
public IdWorker() {
this(Misc.parseLong(System.getProperty("app.workerId"), 0L));
}
/**
* 每个部署环境编号不能重复
*
* @param workerId Worker编号
*/
public IdWorker(long workerId) {
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException(String.format("Worker Id can't be greater than %d or less than 0",
MAX_WORKER_ID));
}
this.workerId = workerId;
}
/**
* 获取下一个ID
*
* @return
*/
public synchronized long next() {
long ts = System.currentTimeMillis();
if (lastTimestamp == ts) {
sequence = (sequence + 1) & SEQUENCE_MASK;
// 当前毫秒的序号已经用完,取下一个毫秒
if (sequence == 0) {
ts = nextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
if (ts < lastTimestamp) {
throw new RuntimeException(String.format(
"Clock moved backwards. Refusing to generate id for %d millseconds", lastTimestamp - ts));
}
lastTimestamp = ts;
return ((ts - BASE_TIMESTAMP) << TIMESTAMP_SHIFT) | (workerId << WORKER_SHIFT) | sequence;
}
// FIXME:当机器时间被调早时,会导致CPU过高。此处可以考虑进行阻塞
private long nextMillis(final long lastTimestamp) {
long ts = System.currentTimeMillis();
while (ts <= lastTimestamp) {
ts = System.currentTimeMillis();
}
return ts;
}
}
import java.io.File;
import java.security.MessageDigest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import com.assess.lang.AppException;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
public final class TextUtil {
public static final int NO_TRIM = 0;
public static final int TRIM_TO_NULL = 1;
public static final int TRIM_TO_BLANK = 2;
/**
* 创建文件目录时使用的质数,默认每个文件夹下使用
*/
private static final int SEED = 8999;
private static final char[] CHARS_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
.toCharArray();
private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
private TextUtil() {
}
/**
* 将日期(时间)格式化成指定格式的字符串
*
* @param date 日期(时间)对象
* @param pattern 格式化模式
* @return
*/
public static String formatTime(Date date, String pattern) {
return new SimpleDateFormat(pattern).format(date);
}
/**
* 将日期(时间)字符串解析成日期时间对象
*
* @param val 日期(时间)字符串
* @param pattern 时间模式
* @return 返回日期对象,如果解析失败,则返回null
*/
public static Date parseTime(String val, String pattern) {
try {
return new SimpleDateFormat(pattern).parse(val);
} catch (ParseException e) {
return null;
}
}
/**
* 使用单个字符分割字符串
*
* @param value
* @param delim
* @return
*/
public static String[] split(String value, char delim) {
return split(value, delim, TRIM_TO_BLANK);
}
/**
* 使用单个字符分割字符串
*
* @param value
* @param delim
* @param trimFlag Trim方式
* @return
*/
public static String[] split(String value, char delim, int trimFlag) {
final int end = value.length();
final List<String> res = new ArrayList<String>();
int start = 0;
for (int i = 0; i < end; i++) {
if (value.charAt(i) == delim) {
if (start == i) {
res.add("");
} else {
res.add(value.substring(start, i));
}
start = i + 1;
}
}
if (start == 0) {
res.add(value);
} else if (start != end) {
res.add(value.substring(start, end));
} else {
for (int i = res.size() - 1; i >= 0; i--) {
if (res.get(i).isEmpty()) {
res.remove(i);
} else {
break;
}
}
}
String[] ret = res.toArray(new String[res.size()]);
if (trimFlag > 0) {
for (int i = 0; i < ret.length; ++i) {
if (trimFlag == TRIM_TO_NULL) {
ret[i] = trimToNull(ret[i]);
} else {
ret[i] = trimToEmpty(ret[i]);
}
}
}
return ret;
}
/**
* 对字符串进行MD5编码后转为16进制字符串
*
* @param src
* @return
*/
public static String md5(String src, String salt) {
return toHexString(digest("MD5", src, salt));
}
/**
* 对对象进行MD5编码后转为16进制字符串
*
* @param src
* @return
*/
public static String md5(Object obj, String salt) {
return toHexString(digest("MD5", toBytes(obj), salt));
}
/**
* 对字符串进行SHA-256编码后转为16进制字符串
*
* @param src
* @return
*/
public static String sha256(String src, String salt) {
return toHexString(digest("SHA-256", src, salt));
}
/**
* 对字符串进行SHA-256编码后转为16进制字符串
*
* @param src
* @return
*/
public static byte[] bSha256(String src, String salt) {
return digest("SHA-256", src, salt);
}
/**
* 合并两个字节数组
*
* @param x
* @param y
* @return
*/
public static byte[] merge(byte[] x, byte[] y) {
byte[] b = new byte[x.length + y.length];
System.arraycopy(x, 0, b, 0, x.length);
System.arraycopy(y, 0, b, x.length, y.length);
return b;
}
/**
* 转换字节数组为16进制字串
*
* @param b 字节数组
* @return 16进制字串
*/
public static String toHexString(byte[] b) {
StringBuilder buf = new StringBuilder(b.length * 2);
int n = 0;
for (int i = 0; i < b.length; i++) {
n = b[i];
if (n < 0) {
n += 256;
}
buf.append(HEX_DIGITS[n >> 4]).append(HEX_DIGITS[n % 16]);
}
return buf.toString();
}
/**
* 根据给定的key和seed,生成唯一的有效文件路径。 有效文件路径需要满足: 1、每个文件夹下不超过10000个文件; 2、可以根据文件路径和seed还原key
*
* @param key 用来生成文件路径的一个唯一数值,一般为文件的ID
* @param deep 生成文件路径的有效深度,实际生成的路径深度为deep+1
* @return
*/
public static String dir(long key) {
return dir(key, 2, SEED);
}
/**
* 根据给定的key和seed,生成唯一的有效文件路径。 有效文件路径需要满足: 1、每个文件夹下不超过10000个文件; 2、可以根据文件路径和seed还原key
*
* @param key 用来生成文件路径的一个唯一数值,一般为文件的ID
* @param deep 生成文件路径的有效深度,实际生成的路径深度为deep+1
* @return
*/
public static String dir(long key, int deep) {
return dir(key, deep, SEED);
}
/**
* 根据给定的key和seed,生成唯一的有效文件路径。 有效文件路径需要满足: 1、每个文件夹下不超过10000个文件; 2、可以根据文件路径和seed还原key
*
* @param key 用来生成文件路径的一个唯一数值,一般为文件的ID
* @param deep 生成文件路径的有效深度,实际生成的路径深度为deep+1
* @param seed 文件夹最大数量,有效取值为小于10000的质数
* @return
*/
public static String dir(long key, int deep, long seed) {
StringBuilder buf = new StringBuilder(32);
for (int i = 0; (i < deep) && (key > 0); i++) {
base62(buf, key % seed);
buf.append(File.separatorChar);
key = key / seed;
}
return buf.append(key).toString();
}
/**
* 生成随机的字符串,字符串有效字符为数字和字母
*
* @param length 字符串长度
* @return
*/
public static String salt(int length) {
StringBuilder buf = new StringBuilder(length);
for (int i = 0; i < length; i++) {
buf.append(CHARS_DIGITS[(int) (Math.random() * CHARS_DIGITS.length)]);
}
return buf.toString();
}
public static boolean isBlank(CharSequence cs) {
int strLen;
if ((cs == null) || ((strLen = cs.length()) == 0)) {
return true;
}
for (int i = 0; i < strLen; i++) {
if (Character.isWhitespace(cs.charAt(i)) == false) {
return false;
}
}
return true;
}
public static boolean isBlank(CharSequence cs, int end) {
int strLen;
if ((cs == null) || ((strLen = cs.length()) == 0)) {
return true;
}
if ((end > 0) && (end < strLen)) {
strLen = end;
}
for (int i = 0; i < strLen; i++) {
if (Character.isWhitespace(cs.charAt(i)) == false) {
return false;
}
}
return true;
}
public static boolean isNotBlank(CharSequence cs) {
return !isBlank(cs);
}
public static boolean isEmpty(CharSequence cs) {
return (cs == null) || (cs.length() == 0);
}
public static boolean startsWith(String s, char c) {
if (s.length() == 0) {
return false;
}
return s.charAt(0) == c;
}
public static boolean endsWith(String s, char c) {
if (s.length() == 0) {
return false;
}
return s.charAt(s.length() - 1) == c;
}
public static String trim(String cs) {
return cs == null ? null : cs.trim();
}
public static String trimToNull(String cs) {
String s = trim(cs);
return isEmpty(s) ? null : s;
}
public static String trimToEmpty(String cs) {
String s = trim(cs);
return s == null ? "" : s;
}
public static String trimToFlag(String src, String flag) {
int index = src.indexOf(flag);
if (index == -1) {
return src;
}
return src.substring(0, index);
}
public static Long toLong(Object o) {
if (o instanceof Long) {
return (Long) o;
}
if (o instanceof String) {
return Misc.parseLong((String) o, 0L);
}
if (o instanceof Integer) {
return ((Integer) o).longValue();
}
return null;
}
public static Integer toInteger(Object o) {
if (o instanceof Long) {
return ((Long) o).intValue();
}
if (o instanceof String) {
return Misc.parseInt((String) o, 0);
}
if (o instanceof Integer) {
return (Integer) o;
}
return null;
}
@SuppressWarnings("unchecked")
public static List<Long> toLongList(Object o) {
if (!(o instanceof List)) {
return Collections.EMPTY_LIST;
}
List<Object> list = (List<Object>) o;
List<Long> result = new ArrayList<Long>(list.size());
for (Object e : list) {
result.add(toLong(e));
}
return result;
}
@SuppressWarnings("unchecked")
public static List<Integer> toIntegerList(Object o) {
if (!(o instanceof List)) {
return Collections.EMPTY_LIST;
}
List<Object> list = (List<Object>) o;
List<Integer> result = new ArrayList<Integer>(list.size());
for (Object e : list) {
result.add(toInteger(e));
}
return result;
}
public static String like(String src) {
return like(src, LikeType.ALL);
}
public static String like(String src, LikeType type) {
src = trim(src);
if (src == null) {
return src;
}
if (type == LikeType.LEFT) {
return "%" + src;
}
if (type == LikeType.RIGHT) {
return src + "%";
}
return "%" + src + "%";
}
private static void base62(StringBuilder buf, long val) {
while (val > 0) {
buf.append(CHARS_DIGITS[(int) (val % 62)]);
val /= 62;
}
}
private static byte[] digest(String algorithm, String src, String salt) {
return digest(algorithm, src.getBytes(), salt);
}
private static byte[] digest(String algorithm, byte[] bytes, String salt) {
try {
if ((salt != null) && (salt.length() > 0)) {
bytes = merge(bytes, salt.getBytes());
}
bytes = MessageDigest.getInstance(algorithm).digest(bytes);
} catch (Exception e) {
// 忽略异常
}
return bytes;
}
public static byte[] toBytes(Object obj) {
try {
Output output = new Output(new byte[2048], -1);
new Kryo().writeObject(output, obj);
output.flush();
return output.toBytes();
} catch (Exception e) {
throw new AppException("Serialize Object failed", e);
}
}
public static <T> T readBytes(byte[] bytes, Class<T> clazz) {
if (bytes == null) {
return null;
}
try {
return new Kryo().readObject(new Input(bytes, 0, bytes.length), clazz);
} catch (Exception e) {
return null;
}
}
public static byte[] int2Bytes(int num) {
byte[] byteNum = new byte[4];
for (int ix = 0; ix < 4; ++ix) {
int offset = 32 - (ix + 1) * 8;
byteNum[ix] = (byte) ((num >> offset) & 0xff);
}
return byteNum;
}
public static int bytes2Int(byte[] byteNum) {
int num = 0;
for (int ix = 0; ix < 4; ++ix) {
num <<= 8;
num |= (byteNum[ix] & 0xff);
}
return num;
}
public static byte[] long2Bytes(long num) {
byte[] byteNum = new byte[8];
for (int ix = 0; ix < 8; ++ix) {
int offset = 64 - (ix + 1) * 8;
byteNum[ix] = (byte) ((num >> offset) & 0xff);
}
return byteNum;
}
public static long bytes2Long(byte[] byteNum) {
long num = 0;
for (int ix = 0; ix < 8; ++ix) {
num <<= 8;
num |= (byteNum[ix] & 0xff);
}
return num;
}
public static enum LikeType {
LEFT, RIGHT, ALL
}
}
public final class Misc {
private Misc() {
}
/**
* 判断集合是否为null或空
*
* @param c
* @return
*/
public static boolean isEmpty(Collection<?> c) {
return ((c == null) || (c.size() == 0));
}
/**
* 将字符串按','分割后存入Set
*
* @param s
* @return
*/
public static Set<String> asSet(String s) {
if ((s == null) || (s.trim().length() == 0)) {
return null;
}
String[] parts = TextUtil.split(s, ',');
Set<String> set = new HashSet<String>(parts.length);
for (String part : parts) {
String trimmed = part.trim();
if (trimmed.length() > 0) {
set.add(trimmed);
}
}
return set;
}
/**
* 生成上传文件的临时保存路径,并使用UUID对上传文件重命名。
*
* @param dir 临时文件保存的目录。使用系统绝对路径,一般使用"
* @param suffix 文件名后缀
* @param max 最大重试次数
* @return 创建成功时返回文件的绝对路径,创建失败时返回null
*/
public static File randomFile(String dir, String suffix, int max, boolean create) throws IOException {
File p = new File(dir, TextUtil.formatTime(new Date(), "yyyyMMdd"));
if (p.exists() || p.mkdirs()) {
for (int i = 0; i < max; ++i) {
File f = new File(p, TextUtil.salt(8) + suffix);
if (!f.exists()) {
if (create && f.createNewFile()) {
return f;
}
}
}
}
return null;
}
/**
* 安全地将字符串解析成整型
*
* @param s 字符串
* @param defaultValue 字符串为空(包括null)或解析失败时返回默认值
* @return
*/
public static int parseInt(String s, int defaultValue) {
if ((s != null) && (s.length() > 0)) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
// 忽略异常
}
}
return defaultValue;
}
/**
* 安全地将字符串解析成长整型
*
* @param s 字符串
* @param defaultValue 字符串为空(包括null)或解析失败时返回默认值
* @return
*/
public static long parseLong(String s, long defaultValue) {
if ((s != null) && (s.length() > 0)) {
try {
return Long.parseLong(s);
} catch (NumberFormatException e) {
// 忽略异常
}
}
return defaultValue;
}
/**
* 获取远程客户端的IP地址
*
* @param request
* @return 没有找到ip时返回null
*/
public static String parseIp(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
if (TextUtil.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
ip = request.getHeader("X-Forwarded-For");
if (!TextUtil.isBlank(ip)) {
for (String value : TextUtil.split(ip, ',', TextUtil.TRIM_TO_NULL)) {
if (value != null) {
if ("unknown".equalsIgnoreCase(ip)) {
break;
}
return value;
}
}
}
return request.getRemoteAddr();
}
}