map) {
+ R r = new R();
+ r.putAll(map);
+ return r;
+ }
+
+ public static R ok() {
+ return new R();
+ }
+
+ @Override
+ public R put(String key, Object value) {
+ super.put(key, value);
+ return this;
+ }
+}
diff --git a/renren-generator/src/main/java/io/renren/utils/RenException.java b/renren-generator/src/main/java/io/renren/utils/RenException.java
new file mode 100644
index 0000000..0275a4a
--- /dev/null
+++ b/renren-generator/src/main/java/io/renren/utils/RenException.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2018 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.utils;
+
+/**
+ * 自定义异常
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class RenException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ private String msg;
+ private int code = 500;
+
+ public RenException(String msg) {
+ super(msg);
+ this.msg = msg;
+ }
+
+ public RenException(String msg, Throwable e) {
+ super(msg, e);
+ this.msg = msg;
+ }
+
+ public RenException(String msg, int code) {
+ super(msg);
+ this.msg = msg;
+ this.code = code;
+ }
+
+ public RenException(String msg, int code, Throwable e) {
+ super(msg, e);
+ this.msg = msg;
+ this.code = code;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+
+}
diff --git a/renren-generator/src/main/java/io/renren/utils/RenExceptionHandler.java b/renren-generator/src/main/java/io/renren/utils/RenExceptionHandler.java
new file mode 100644
index 0000000..27a3822
--- /dev/null
+++ b/renren-generator/src/main/java/io/renren/utils/RenExceptionHandler.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2018 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+/**
+ * 异常处理器
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@RestControllerAdvice
+public class RenExceptionHandler {
+ private Logger logger = LoggerFactory.getLogger(getClass());
+
+ /**
+ * 处理自定义异常
+ */
+ @ExceptionHandler(RenException.class)
+ public R handleRenException(RenException e){
+ R r = new R();
+ r.put("code", e.getCode());
+ r.put("msg", e.getMessage());
+
+ return r;
+ }
+
+ @ExceptionHandler(Exception.class)
+ public R handleException(Exception e){
+ logger.error(e.getMessage(), e);
+ return R.error();
+ }
+}
diff --git a/renren-generator/src/main/java/io/renren/utils/Sequence.java b/renren-generator/src/main/java/io/renren/utils/Sequence.java
new file mode 100644
index 0000000..21a3b9b
--- /dev/null
+++ b/renren-generator/src/main/java/io/renren/utils/Sequence.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ *
+ * Licensed 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 io.renren.utils;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.LogFactory;
+
+import java.lang.management.ManagementFactory;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ *
+ * 分布式高效有序ID生产黑科技(sequence)
+ * 优化开源项目:http://git.oschina.net/yu120/sequence
+ *
+ *
+ * @author hubin
+ * @since 2016-08-18
+ */
+public class Sequence {
+
+ private static final Log logger = LogFactory.getLog(Sequence.class);
+ /**
+ * 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
+ */
+ private final long twepoch = 1288834974657L;
+ /**
+ * 机器标识位数
+ */
+ private final long workerIdBits = 5L;
+ private final long datacenterIdBits = 5L;
+ private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
+ private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
+ /**
+ * 毫秒内自增位
+ */
+ private final long sequenceBits = 12L;
+ private final long workerIdShift = sequenceBits;
+ private final long datacenterIdShift = sequenceBits + workerIdBits;
+ /**
+ * 时间戳左移动位
+ */
+ private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
+ private final long sequenceMask = -1L ^ (-1L << sequenceBits);
+
+ private final long workerId;
+
+ /**
+ * 数据标识 ID 部分
+ */
+ private final long datacenterId;
+ /**
+ * 并发控制
+ */
+ private long sequence = 0L;
+ /**
+ * 上次生产 ID 时间戳
+ */
+ private long lastTimestamp = -1L;
+
+ public Sequence() {
+ this.datacenterId = getDatacenterId(maxDatacenterId);
+ this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
+ }
+
+ /**
+ *
+ * 有参构造器
+ *
+ *
+ * @param workerId 工作机器 ID
+ * @param datacenterId 序列号
+ */
+ public Sequence(long workerId, long datacenterId) {
+ Assert.isFalse(workerId > maxWorkerId || workerId < 0,
+ String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
+ Assert.isFalse(datacenterId > maxDatacenterId || datacenterId < 0,
+ String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
+ this.workerId = workerId;
+ this.datacenterId = datacenterId;
+ }
+
+ /**
+ *
+ * 获取 maxWorkerId
+ *
+ */
+ protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
+ StringBuilder mpid = new StringBuilder();
+ mpid.append(datacenterId);
+ String name = ManagementFactory.getRuntimeMXBean().getName();
+ if (StringUtils.isNotEmpty(name)) {
+ /*
+ * GET jvmPid
+ */
+ mpid.append(name.split(StringPool.AT)[0]);
+ }
+ /*
+ * MAC + PID 的 hashcode 获取16个低位
+ */
+ return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
+ }
+
+ /**
+ *
+ * 数据标识id部分
+ *
+ */
+ protected static long getDatacenterId(long maxDatacenterId) {
+ long id = 0L;
+ try {
+ InetAddress ip = InetAddress.getLocalHost();
+ NetworkInterface network = NetworkInterface.getByInetAddress(ip);
+ if (network == null) {
+ id = 1L;
+ } else {
+ byte[] mac = network.getHardwareAddress();
+ if (null != mac) {
+ id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
+ id = id % (maxDatacenterId + 1);
+ }
+ }
+ } catch (Exception e) {
+ logger.warn(" getDatacenterId: " + e.getMessage());
+ }
+ return id;
+ }
+
+ /**
+ * 获取下一个ID
+ *
+ * @return
+ */
+ public synchronized long nextId() {
+ long timestamp = timeGen();
+ //闰秒
+ if (timestamp < lastTimestamp) {
+ long offset = lastTimestamp - timestamp;
+ if (offset <= 5) {
+ try {
+ wait(offset << 1);
+ timestamp = timeGen();
+ if (timestamp < lastTimestamp) {
+ throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset));
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset));
+ }
+ }
+
+ if (lastTimestamp == timestamp) {
+ // 相同毫秒内,序列号自增
+ sequence = (sequence + 1) & sequenceMask;
+ if (sequence == 0) {
+ // 同一毫秒的序列数已经达到最大
+ timestamp = tilNextMillis(lastTimestamp);
+ }
+ } else {
+ // 不同毫秒内,序列号置为 1 - 3 随机数
+ sequence = ThreadLocalRandom.current().nextLong(1, 3);
+ }
+
+ lastTimestamp = timestamp;
+
+ // 时间戳部分 | 数据中心部分 | 机器标识部分 | 序列号部分
+ return ((timestamp - twepoch) << timestampLeftShift)
+ | (datacenterId << datacenterIdShift)
+ | (workerId << workerIdShift)
+ | sequence;
+ }
+
+ protected long tilNextMillis(long lastTimestamp) {
+ long timestamp = timeGen();
+ while (timestamp <= lastTimestamp) {
+ timestamp = timeGen();
+ }
+ return timestamp;
+ }
+
+ protected long timeGen() {
+ return SystemClock.now();
+ }
+
+}
diff --git a/renren-generator/src/main/java/io/renren/utils/StringPool.java b/renren-generator/src/main/java/io/renren/utils/StringPool.java
new file mode 100644
index 0000000..f8bcc27
--- /dev/null
+++ b/renren-generator/src/main/java/io/renren/utils/StringPool.java
@@ -0,0 +1,95 @@
+package io.renren.utils;
+
+/**
+ * Copy to jodd.util
+ *
+ * Pool of String constants to prevent repeating of
+ * hard-coded String literals in the code.
+ * Due to fact that these are public static final
+ * they will be inlined by java compiler and
+ * reference to this class will be dropped.
+ * There is no performance gain of using this pool.
+ * Read: http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.5
+ *
+ * - Literal strings within the same class in the same package represent references to the same
String object.
+ * - Literal strings within different classes in the same package represent references to the same
String object.
+ * - Literal strings within different classes in different packages likewise represent references to the same
String object.
+ * - Strings computed by constant expressions are computed at compile time and then treated as if they were literals.
+ * - Strings computed by concatenation at run time are newly created and therefore distinct.
+ *
+ */
+public interface StringPool {
+
+ String AMPERSAND = "&";
+ String AND = "and";
+ String AT = "@";
+ String ASTERISK = "*";
+ String STAR = ASTERISK;
+ String BACK_SLASH = "\\";
+ String COLON = ":";
+ String COMMA = ",";
+ String DASH = "-";
+ String DOLLAR = "$";
+ String DOT = ".";
+ String DOTDOT = "..";
+ String DOT_CLASS = ".class";
+ String DOT_JAVA = ".java";
+ String DOT_XML = ".xml";
+ String EMPTY = "";
+ String EQUALS = "=";
+ String FALSE = "false";
+ String SLASH = "/";
+ String HASH = "#";
+ String HAT = "^";
+ String LEFT_BRACE = "{";
+ String LEFT_BRACKET = "(";
+ String LEFT_CHEV = "<";
+ String NEWLINE = "\n";
+ String N = "n";
+ String NO = "no";
+ String NULL = "null";
+ String OFF = "off";
+ String ON = "on";
+ String PERCENT = "%";
+ String PIPE = "|";
+ String PLUS = "+";
+ String QUESTION_MARK = "?";
+ String EXCLAMATION_MARK = "!";
+ String QUOTE = "\"";
+ String RETURN = "\r";
+ String TAB = "\t";
+ String RIGHT_BRACE = "}";
+ String RIGHT_BRACKET = ")";
+ String RIGHT_CHEV = ">";
+ String SEMICOLON = ";";
+ String SINGLE_QUOTE = "'";
+ String BACKTICK = "`";
+ String SPACE = " ";
+ String TILDA = "~";
+ String LEFT_SQ_BRACKET = "[";
+ String RIGHT_SQ_BRACKET = "]";
+ String TRUE = "true";
+ String UNDERSCORE = "_";
+ String UTF_8 = "UTF-8";
+ String US_ASCII = "US-ASCII";
+ String ISO_8859_1 = "ISO-8859-1";
+ String Y = "y";
+ String YES = "yes";
+ String ONE = "1";
+ String ZERO = "0";
+ String DOLLAR_LEFT_BRACE = "${";
+ String HASH_LEFT_BRACE = "#{";
+ String CRLF = "\r\n";
+
+ String HTML_NBSP = " ";
+ String HTML_AMP = "&";
+ String HTML_QUOTE = """;
+ String HTML_LT = "<";
+ String HTML_GT = ">";
+
+ // ---------------------------------------------------------------- array
+
+ String[] EMPTY_ARRAY = new String[0];
+
+ byte[] BYTES_NEW_LINE = StringPool.NEWLINE.getBytes();
+}
diff --git a/renren-generator/src/main/java/io/renren/utils/SystemClock.java b/renren-generator/src/main/java/io/renren/utils/SystemClock.java
new file mode 100644
index 0000000..6d614a6
--- /dev/null
+++ b/renren-generator/src/main/java/io/renren/utils/SystemClock.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ *
+ * Licensed 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 io.renren.utils;
+
+import java.sql.Timestamp;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ *
+ * 高并发场景下System.currentTimeMillis()的性能问题的优化
+ *
+ *
+ * System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我还没测试过,有人说是100倍左右)
+ * System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道
+ * 后台定时更新时钟,JVM退出时,线程自动回收
+ * 10亿:43410,206,210.72815533980582%
+ * 1亿:4699,29,162.0344827586207%
+ * 1000万:480,12,40.0%
+ * 100万:50,10,5.0%
+ *
+ *
+ * @author hubin
+ * @since 2016-08-01
+ */
+public class SystemClock {
+
+ private final long period;
+ private final AtomicLong now;
+
+ private SystemClock(long period) {
+ this.period = period;
+ this.now = new AtomicLong(System.currentTimeMillis());
+ scheduleClockUpdating();
+ }
+
+ private static SystemClock instance() {
+ return InstanceHolder.INSTANCE;
+ }
+
+ public static long now() {
+ return instance().currentTimeMillis();
+ }
+
+ public static String nowDate() {
+ return new Timestamp(instance().currentTimeMillis()).toString();
+ }
+
+ private void scheduleClockUpdating() {
+ ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> {
+ Thread thread = new Thread(runnable, "System Clock");
+ thread.setDaemon(true);
+ return thread;
+ });
+ scheduler.scheduleAtFixedRate(() -> now.set(System.currentTimeMillis()), period, period, TimeUnit.MILLISECONDS);
+ }
+
+ private long currentTimeMillis() {
+ return now.get();
+ }
+
+ private static class InstanceHolder {
+ public static final SystemClock INSTANCE = new SystemClock(1);
+ }
+}