分布式非公平锁实现、路径参数正则表达式修改增强
parent
50989aa997
commit
2d4bce9513
|
@ -186,7 +186,7 @@ public class Util extends weaver.general.Util {
|
|||
String str = sqlBuilder.toString().trim();
|
||||
String removeSeparator = ",";
|
||||
if (str.endsWith(removeSeparator)) {
|
||||
// 如果以分割号结尾,则去除分割号
|
||||
// 如果以分割号结尾,则去除分割号
|
||||
str = str.substring(0, str.length() - 1);
|
||||
}
|
||||
if (str.trim().startsWith(removeSeparator)) {
|
||||
|
@ -205,7 +205,7 @@ public class Util extends weaver.general.Util {
|
|||
public static String removeSeparator(StringBuilder sqlBuilder, String removeSeparator) {
|
||||
String str = sqlBuilder.toString().trim();
|
||||
if (str.endsWith(removeSeparator)) {
|
||||
// 如果以分割号结尾,则去除分割号
|
||||
// 如果以分割号结尾,则去除分割号
|
||||
str = str.substring(0, str.length() - 1);
|
||||
}
|
||||
if (str.trim().startsWith(removeSeparator)) {
|
||||
|
@ -224,7 +224,7 @@ public class Util extends weaver.general.Util {
|
|||
public static String removeSeparator(String string, String removeSeparator) {
|
||||
String str = string.trim();
|
||||
if (str.endsWith(removeSeparator)) {
|
||||
// 如果以分割号结尾,则去除分割号
|
||||
// 如果以分割号结尾,则去除分割号
|
||||
str = str.substring(0, str.length() - 1);
|
||||
}
|
||||
if (str.trim().startsWith(removeSeparator)) {
|
||||
|
@ -1212,8 +1212,8 @@ public class Util extends weaver.general.Util {
|
|||
inputStream = new BufferedInputStream(new FileInputStream(path));
|
||||
is = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
||||
prop.load(is);
|
||||
// Enumeration<?> enumeration = prop.propertyNames();
|
||||
// 顺序读取
|
||||
// Enumeration<?> enumeration = prop.propertyNames();
|
||||
// 顺序读取
|
||||
Enumeration<?> enumeration = prop.keys();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
String key = (String) enumeration.nextElement();
|
||||
|
@ -1256,7 +1256,7 @@ public class Util extends weaver.general.Util {
|
|||
Pattern compile = Pattern.compile(objRegex);
|
||||
Matcher matcher = compile.matcher(key);
|
||||
if (matcher.find()) {
|
||||
// 只匹配前缀.key=value模式的
|
||||
// 只匹配前缀.key=value模式的
|
||||
String resultKey = matcher.group("key");
|
||||
preResult.put(resultKey, prop2MapPutValue(value));
|
||||
}
|
||||
|
@ -1264,7 +1264,7 @@ public class Util extends weaver.general.Util {
|
|||
compile = Pattern.compile(moreKey);
|
||||
matcher = compile.matcher(key);
|
||||
if (matcher.find()) {
|
||||
// 匹配前缀.key1.key2=value模式的
|
||||
// 匹配前缀.key1.key2=value模式的
|
||||
String objKey = matcher.group("objKey");
|
||||
String prefixStr = prePrefix + "." + objKey;
|
||||
Map<String, Object> valueMap;
|
||||
|
@ -1282,11 +1282,11 @@ public class Util extends weaver.general.Util {
|
|||
compile = Pattern.compile(strList);
|
||||
matcher = compile.matcher(key);
|
||||
if (matcher.find()) {
|
||||
// 匹配前缀.key[0]=value模式的
|
||||
// 匹配前缀.key[0]=value模式的
|
||||
String objKey = matcher.group("key");
|
||||
int index = Integer.parseInt(matcher.group("index"));
|
||||
if (preResult.containsKey(objKey)) {
|
||||
// 存在值
|
||||
// 存在值
|
||||
List<Object> valueList = (List<Object>) preResult.get(objKey);
|
||||
if (index >= valueList.size()) {
|
||||
valueList.add(prop2MapPutValue(value));
|
||||
|
@ -1301,19 +1301,19 @@ public class Util extends weaver.general.Util {
|
|||
return null;
|
||||
}
|
||||
String objArray = "^(" + prePrefix + "\\.)(?<arrKey>(\\w+))(\\[(?<index>([0-9])+)])\\.(?<objKey>(\\S)+)$";
|
||||
// String objArray = "^("+prePrefix+"\\.)(?<arrKey>(\\w+))(\\[(?<index>([0-9])+)])(\\.(?<objKey>(\\S)+))+";
|
||||
// String objArray = "^("+prePrefix+"\\.)(?<arrKey>(\\w+))(\\[(?<index>([0-9])+)])(\\.(?<objKey>(\\S)+))+";
|
||||
compile = Pattern.compile(objArray);
|
||||
matcher = compile.matcher(key);
|
||||
if (matcher.find()) {
|
||||
// 匹配前缀.key[0].name=value的模式
|
||||
// 匹配前缀.key[0].name=value的模式
|
||||
String arrKey = matcher.group("arrKey");
|
||||
String objKey = matcher.group("objKey");
|
||||
int index = Integer.parseInt(matcher.group("index"));
|
||||
List<Map<String, Object>> mapList;
|
||||
if (preResult.containsKey(arrKey)) {
|
||||
// 存在
|
||||
// 存在
|
||||
mapList = (List<Map<String, Object>>) preResult.get(arrKey);
|
||||
// mapList
|
||||
// mapList
|
||||
Map<String, Object> valueMap;
|
||||
if (index >= mapList.size()) {
|
||||
valueMap = new HashMap<>();
|
||||
|
@ -1346,7 +1346,7 @@ public class Util extends weaver.general.Util {
|
|||
int arrMoreIndex = Integer.parseInt(arrMoreKeyMatcher.group("index"));
|
||||
List<Object> arrMoreListValue;
|
||||
if (valueMap.containsKey(arrMoreArrKey)) {
|
||||
// 存在值
|
||||
// 存在值
|
||||
arrMoreListValue = (List<Object>) valueMap.get(arrMoreArrKey);
|
||||
if (arrMoreIndex >= arrMoreListValue.size()) {
|
||||
arrMoreListValue.add(prop2MapPutValue(value));
|
||||
|
@ -1361,11 +1361,11 @@ public class Util extends weaver.general.Util {
|
|||
return null;
|
||||
}
|
||||
|
||||
// 直接添加
|
||||
// 直接添加
|
||||
valueMap.put(objKey, prop2MapPutValue(value));
|
||||
return null;
|
||||
}
|
||||
// 不存在
|
||||
// 不存在
|
||||
mapList = new ArrayList<>();
|
||||
Map<String, Object> valueMap = new HashMap<>();
|
||||
valueMap.put(objKey, prop2MapPutValue(value));
|
||||
|
@ -1494,7 +1494,7 @@ public class Util extends weaver.general.Util {
|
|||
//
|
||||
private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
|
||||
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
|
||||
// putIfAbsent添加不存在的键,返回null,如果为null表示不重复
|
||||
// putIfAbsent添加不存在的键,返回null,如果为null表示不重复
|
||||
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
|
||||
}
|
||||
|
||||
|
@ -1585,7 +1585,7 @@ public class Util extends weaver.general.Util {
|
|||
for (int i = 0; i < inputList.size(); i++) {
|
||||
T item = inputList.get(i);
|
||||
if (item instanceof InputStream) {
|
||||
// 属于单级文件,直接压缩并返回
|
||||
// 属于单级文件,直接压缩并返回
|
||||
try {
|
||||
zipOut.putNextEntry(new ZipEntry(base + i));
|
||||
} catch (IOException e) {
|
||||
|
@ -1618,7 +1618,7 @@ public class Util extends weaver.general.Util {
|
|||
if (item instanceof ListZipEntity) {
|
||||
ListZipEntity listZipEntity = (ListZipEntity) item;
|
||||
if (listZipEntity.isDirectory()) {
|
||||
// 表示属于文件夹,循环添加处理文件夹
|
||||
// 表示属于文件夹,循环添加处理文件夹
|
||||
handlerDirectory(listZipEntity.getFileList(), zipOut, base + listZipEntity.getDirectory() + File.separator);
|
||||
} else {
|
||||
List<AInputStream> aInputStreams = listZipEntity.getaInputStreamList();
|
||||
|
@ -1649,7 +1649,7 @@ public class Util extends weaver.general.Util {
|
|||
int catchLen = 10 * 1024;
|
||||
for (ListZipEntity listZipEntity : fileList) {
|
||||
if (listZipEntity.isDirectory()) {
|
||||
// 如果是文件夹
|
||||
// 如果是文件夹
|
||||
handlerDirectory(listZipEntity.getFileList(), zipOut, base + listZipEntity.getDirectory() + File.separator);
|
||||
} else {
|
||||
List<AInputStream> aInputStreams = listZipEntity.getaInputStreamList();
|
||||
|
@ -1759,8 +1759,8 @@ public class Util extends weaver.general.Util {
|
|||
@Deprecated
|
||||
public static String getDocCategorysById(String workflowId, String docFieldId) {
|
||||
RecordSet rs = new RecordSet();
|
||||
// rs.executeQuery("select formid from workflow_base where id = ?",workflowId);
|
||||
// String formId = Util.recordeSet2Entity(rs, String.class);
|
||||
// rs.executeQuery("select formid from workflow_base where id = ?",workflowId);
|
||||
// String formId = Util.recordeSet2Entity(rs, String.class);
|
||||
String query = "select doccategory from workflow_fileupload where workflowid = ? and fieldid = ?";
|
||||
rs.executeQuery(query, workflowId, docFieldId);
|
||||
String docCategorys = Util.null2String(Util.recordeSet2Entity(rs, String.class));
|
||||
|
@ -2303,7 +2303,7 @@ public class Util extends weaver.general.Util {
|
|||
continue;
|
||||
}
|
||||
dataMap.put(id, item);
|
||||
// 判断是否属于根节点,如果是根节点,则将数据添加到树中
|
||||
// 判断是否属于根节点,如果是根节点,则将数据添加到树中
|
||||
if (predicate.test(parentId)) {
|
||||
if (childMap.containsKey(id)) {
|
||||
continue;
|
||||
|
@ -2311,29 +2311,29 @@ public class Util extends weaver.general.Util {
|
|||
treeList.add(item);
|
||||
childMap.put(id, item);
|
||||
} else {
|
||||
// 如果不是根节点,则通过id查找到父级
|
||||
// 如果不是根节点,则通过id查找到父级
|
||||
T parent = dataMap.get(parentId);
|
||||
if (Objects.isNull(parent)) {
|
||||
// 如果父级为空,则说明他的父级还没有遍历到,需要从之后的数据进行遍历,直到找到父级为止
|
||||
// 如果父级为空,则说明他的父级还没有遍历到,需要从之后的数据进行遍历,直到找到父级为止
|
||||
List<T> list = buildTree(dataList, dataMap, childMap, index, getIdFn, getParentId, setChildFn, predicate);
|
||||
parent = dataMap.get(parentId);
|
||||
if (Objects.isNull(parent)) {
|
||||
// 如果还是没有查询到父节点,则表明是顶层节点,将他添加到顶层节点中
|
||||
// 如果还是没有查询到父节点,则表明是顶层节点,将他添加到顶层节点中
|
||||
treeList.add(item);
|
||||
} else {
|
||||
// 如果找到了父节点,则将自己挂到父节点上
|
||||
// 如果找到了父节点,则将自己挂到父节点上
|
||||
if (childMap.containsKey(id)) {
|
||||
continue;
|
||||
}
|
||||
setChildFn.accept(parent, item);
|
||||
childMap.put(id, item);
|
||||
}
|
||||
// 如果查找的list不为空并且有值,那就说明属于根节点
|
||||
// 如果查找的list不为空并且有值,那就说明属于根节点
|
||||
if (list != null && list.size() > 0) {
|
||||
treeList.addAll(list);
|
||||
}
|
||||
} else {
|
||||
// 如果找到了父节点,则将自己挂到父节点上
|
||||
// 如果找到了父节点,则将自己挂到父节点上
|
||||
if (childMap.containsKey(id)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -2375,7 +2375,7 @@ public class Util extends weaver.general.Util {
|
|||
continue;
|
||||
}
|
||||
dataMap.put(id, item);
|
||||
// 判断是否属于根节点,如果是根节点,则将数据添加到树中
|
||||
// 判断是否属于根节点,如果是根节点,则将数据添加到树中
|
||||
if (predicate.test(parentId)) {
|
||||
if (childMap.containsKey(id)) {
|
||||
continue;
|
||||
|
@ -2383,17 +2383,17 @@ public class Util extends weaver.general.Util {
|
|||
treeList.add(item);
|
||||
childMap.put(id, item);
|
||||
} else {
|
||||
// 如果不是根节点,则通过id查找到父级
|
||||
// 如果不是根节点,则通过id查找到父级
|
||||
T parent = dataMap.get(parentId);
|
||||
if (Objects.isNull(parent)) {
|
||||
// 如果父级为空,则说明他的父级还没有遍历到,需要从之后的数据进行遍历,直到找到父级为止
|
||||
// 如果父级为空,则说明他的父级还没有遍历到,需要从之后的数据进行遍历,直到找到父级为止
|
||||
List<T> list = buildTree(dataList, dataMap, childMap, index, getIdFn, getParentId, getChildFn, setChildFn, predicate);
|
||||
parent = dataMap.get(parentId);
|
||||
if (Objects.isNull(parent)) {
|
||||
// 如果还是没有查询到父节点,则表明是顶层节点,将他添加到顶层节点中
|
||||
// 如果还是没有查询到父节点,则表明是顶层节点,将他添加到顶层节点中
|
||||
treeList.add(item);
|
||||
} else {
|
||||
// 如果找到了父节点,则将自己挂到父节点上
|
||||
// 如果找到了父节点,则将自己挂到父节点上
|
||||
if (childMap.containsKey(id)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -2402,12 +2402,12 @@ public class Util extends weaver.general.Util {
|
|||
setChildFn.accept(parent, childList);
|
||||
childMap.put(id, item);
|
||||
}
|
||||
// 如果查找的list不为空并且有值,那就说明属于根节点
|
||||
// 如果查找的list不为空并且有值,那就说明属于根节点
|
||||
if (list != null && list.size() > 0) {
|
||||
treeList.addAll(list);
|
||||
}
|
||||
} else {
|
||||
// 如果找到了父节点,则将自己挂到父节点上
|
||||
// 如果找到了父节点,则将自己挂到父节点上
|
||||
if (childMap.containsKey(id)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -2468,7 +2468,7 @@ public class Util extends weaver.general.Util {
|
|||
throw new RuntimeException("invoke method err, cant not invoke method set" + name.substring(0, 1).toUpperCase() + name.substring(1));
|
||||
}
|
||||
}
|
||||
// TODO 复制bean
|
||||
// TODO 复制bean
|
||||
return target;
|
||||
}
|
||||
|
||||
|
@ -3229,7 +3229,7 @@ public class Util extends weaver.general.Util {
|
|||
action = cronJobClass.newInstance();
|
||||
if (declaredFields.length > 0) {
|
||||
for (Field declaredField : declaredFields) {
|
||||
// 必填参数验证
|
||||
// 必填参数验证
|
||||
boolean hasRequiredMark = declaredField.isAnnotationPresent(RequiredMark.class);
|
||||
String name = declaredField.getName();
|
||||
String setMethodName = getSetMethodName(name);
|
||||
|
@ -3310,7 +3310,7 @@ public class Util extends weaver.general.Util {
|
|||
action = actionClass.newInstance();
|
||||
if (declaredFields.length > 0) {
|
||||
for (Field declaredField : declaredFields) {
|
||||
// 必填参数验证
|
||||
// 必填参数验证
|
||||
boolean hasRequiredMark = declaredField.isAnnotationPresent(RequiredMark.class);
|
||||
String name = declaredField.getName();
|
||||
String setMethodName = getSetMethodName(name);
|
||||
|
@ -3458,10 +3458,12 @@ public class Util extends weaver.general.Util {
|
|||
value:#sql{select workcode from hrmresource where id = #{main.zd1} and test = #{h-hah} and a in (${hrmids})}
|
||||
key:hah
|
||||
value:haode*/
|
||||
// 最终通过反射调用weaver.aiyh_jitu.pushdata.service.GetAssignProcessorProcessorImpl类,将参数传递给这个类,
|
||||
// String pattern = "&?(?<key>([#.\\w\\u4E00-\\u9FA5]+))=(?<value>((#(\\{|sql\\{))?([()\\-$#={ }.\\w\\u4E00-\\u9FA5?]+)?}?))&?";
|
||||
// 最终通过反射调用weaver.aiyh_jitu.pushdata.service.GetAssignProcessorProcessorImpl类,将参数传递给这个类,
|
||||
//String pattern = "&?(?<key>([#.\\w\\u4E00-\\u9FA5]+))=" +
|
||||
// "(?<value>(`([\\s():/\\t\\-&*'?$#={ }.\\w\\u4E00-\\u9FA5]*)`|" +
|
||||
// "((#(\\{|sql\\{))?([():/\\-$#={ }.\\w\\u4E00-\\u9FA5?]+)?}?)))&?";
|
||||
String pattern = "&?(?<key>([#.\\w\\u4E00-\\u9FA5]+))=" +
|
||||
"(?<value>((`([():/\\-&?$#={ }.\\w\\u4E00-\\u9FA5?]*)`)|" +
|
||||
"(?<value>(`([^`]*)`|" +
|
||||
"((#(\\{|sql\\{))?([():/\\-$#={ }.\\w\\u4E00-\\u9FA5?]+)?}?)))&?";
|
||||
Pattern compile = Pattern.compile(pattern);
|
||||
Matcher matcher = compile.matcher(paramStr);
|
||||
|
@ -3470,6 +3472,9 @@ public class Util extends weaver.general.Util {
|
|||
while (matcher.find()) {
|
||||
String key = matcher.group("key");
|
||||
String paramValue = matcher.group("value");
|
||||
if (paramValue.startsWith("`") && paramValue.endsWith("`")) {
|
||||
paramValue = paramValue.substring(1, paramValue.length() - 1);
|
||||
}
|
||||
pathParamMap.put(key, paramValue);
|
||||
}
|
||||
return pathParamMap;
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package aiyh.utils.lock;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* <h1>锁参数信息</h1>
|
||||
*
|
||||
* <p>create: 2022-12-11 02:07</p>
|
||||
*
|
||||
* @author youHong.ai
|
||||
*/
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public class LockEntity {
|
||||
/** 锁住次数 */
|
||||
private Integer times;
|
||||
|
||||
/** 锁标识 */
|
||||
private Thread lockMark;
|
||||
|
||||
/** 过期时间 */
|
||||
private Long expirationTime;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package aiyh.utils.lock;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* <h1>锁对应数据库的实体类</h1>
|
||||
*
|
||||
* <p>create: 2022-12-11 00:24</p>
|
||||
*
|
||||
* @author youHong.ai
|
||||
*/
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@ToString
|
||||
public class LockPojo {
|
||||
/**
|
||||
* 数据库id
|
||||
*/
|
||||
private Integer id;
|
||||
|
||||
/** 过期时间 */
|
||||
private Long expirationTime;
|
||||
|
||||
/** 锁的键 */
|
||||
private String lockName;
|
||||
|
||||
/** 锁状态 */
|
||||
private Integer lockStatus;
|
||||
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package aiyh.utils.lock;
|
||||
|
||||
import aiyh.utils.annotation.recordset.ParamMapper;
|
||||
import aiyh.utils.annotation.recordset.Select;
|
||||
import aiyh.utils.annotation.recordset.SqlMapper;
|
||||
import aiyh.utils.annotation.recordset.Update;
|
||||
|
||||
/**
|
||||
* <h1>分布式锁数据库查询</h1>
|
||||
*
|
||||
* <p>create: 2022-12-11 00:22</p>
|
||||
*
|
||||
* @author youHong.ai
|
||||
*/
|
||||
|
||||
@SqlMapper
|
||||
public interface LockUtilMapper {
|
||||
|
||||
|
||||
/**
|
||||
* <h2>根据锁名称获取锁状态</h2>
|
||||
* <i>2022/12/11 00:37</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param lockKey 锁名称
|
||||
* @return LockPojo 锁参数对象
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
@Select("select id, lock_name, lock_status, expiration_time " +
|
||||
"from uf_cus_lock " +
|
||||
"where lock_name = #{lockKey}")
|
||||
LockPojo selectLock(@ParamMapper("lockKey") String lockKey);
|
||||
|
||||
|
||||
/**
|
||||
* <h2>根据锁名称释放锁资源</h2>
|
||||
* <i>2022/12/11 00:46</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param id 锁id
|
||||
* @return boolean 是否更新成功
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
@Update("update uf_cus_lock " +
|
||||
"set lock_status = 0 " +
|
||||
"where id = #{id} " +
|
||||
" and lock_status = 1")
|
||||
boolean unLock(@ParamMapper("id") Integer id);
|
||||
|
||||
/**
|
||||
* <h2>根据锁名称释放锁资源</h2>
|
||||
* <i>2022/12/11 00:46</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param lockKey 锁名称
|
||||
* @return boolean 是否更新成功
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
@Update("update uf_cus_lock " +
|
||||
"set lock_status = 0 " +
|
||||
"where lock_name = #{lockKey} " +
|
||||
" and lock_status = 1")
|
||||
boolean unLock(@ParamMapper("lockKey") String lockKey);
|
||||
|
||||
|
||||
/**
|
||||
* <h2>更新锁状态,锁住资源</h2>
|
||||
* <i>2022/12/11 00:49</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param id 数据id
|
||||
* @param lockKey 锁名称
|
||||
* @param time 过期时间
|
||||
* @return boolean 是否成功锁住资源
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
@Update("update uf_cus_lock " +
|
||||
"set lock_name = #{lockKey}," +
|
||||
" expiration_time = #{time}," +
|
||||
" lock_status = 1 " +
|
||||
"where id = #{id} " +
|
||||
" and (lock_status = 0 or expiration_time < #{time})")
|
||||
boolean lock(@ParamMapper("id") Integer id,
|
||||
@ParamMapper("lockKey") String lockKey,
|
||||
@ParamMapper("time") Long time);
|
||||
|
||||
|
||||
/**
|
||||
* <h2>更新锁过期时间,续期</h2>
|
||||
* <i>2022/12/11 00:52</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param lockKey 锁名称
|
||||
* @param time 过期时间
|
||||
* @return boolean 是否更新成功
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
@Update("update uf_cus_lock " +
|
||||
"set expiration_time = #{time} " +
|
||||
"where lock_name = #{lockKey} " +
|
||||
" and lock_status = 1")
|
||||
boolean updateLockTime(@ParamMapper("lockKey") String lockKey,
|
||||
@ParamMapper("time") Long time);
|
||||
|
||||
|
||||
/**
|
||||
* <h2>对过期锁重新设置锁</h2>
|
||||
* <i>2022/12/11 15:02</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param id 锁id
|
||||
* @param lockKey 锁名称
|
||||
* @param time 锁过期时间
|
||||
* @param expirationTime 上一个锁的过期时间
|
||||
* @return boolean 是否重新上锁成功
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
@Update("update uf_cus_lock " +
|
||||
"set lock_name = #{lockKey}, " +
|
||||
" expiration_time = #{time}, " +
|
||||
" lock_status = 1 " +
|
||||
"where id = #{id} " +
|
||||
" and lock_status = 1 " +
|
||||
" and expiration_time = #{expirationTime}")
|
||||
boolean reLock(@ParamMapper("id") Integer id,
|
||||
@ParamMapper("lockKey") String lockKey,
|
||||
@ParamMapper("time") Long time,
|
||||
@ParamMapper("expirationTime") Long expirationTime);
|
||||
}
|
|
@ -0,0 +1,346 @@
|
|||
package aiyh.utils.lock;
|
||||
|
||||
import aiyh.utils.Util;
|
||||
import aiyh.utils.excention.CustomerException;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import ebu7common.youhong.ai.bean.Builder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <h1>分布式锁</h1>
|
||||
*
|
||||
* <p>create: 2022-12-11 00:21</p>
|
||||
*
|
||||
* @author youHong.ai
|
||||
*/
|
||||
|
||||
public class LockUtils {
|
||||
|
||||
private static final LockUtilMapper mapper = Util.getMapper(LockUtilMapper.class);
|
||||
|
||||
private static final Map<String, LockEntity> LOCK_MAP = new HashMap<>();
|
||||
|
||||
/** 默认过期时间 10s */
|
||||
private static final Long DEFAULT_OVERDUE_TIME = 1_000 * 10L;
|
||||
|
||||
private static final String UF_CUS_LOCK = "uf_cus_lock";
|
||||
|
||||
/**
|
||||
* <h2>上锁</h2>
|
||||
* <i>2022/12/11 15:40</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param lockName 锁名称
|
||||
* @param reentrant 是否可重入
|
||||
* @return boolean 是否抢占成功
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
public static boolean lock(String lockName, boolean reentrant) {
|
||||
return lock(lockName, DEFAULT_OVERDUE_TIME, reentrant);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <h2>上锁</h2>
|
||||
* <i>2022/12/11 15:40</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param lockName 锁名称
|
||||
* @param overdueTime 超时时间
|
||||
* @param reentrant 是否可重入
|
||||
* @return boolean 是否抢占成功
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
public static boolean lock(String lockName, Long overdueTime, boolean reentrant) {
|
||||
LockPojo lockPojo = mapper.selectLock(lockName);
|
||||
if (Objects.isNull(lockPojo) || lockPojo.getId() <= 0) {
|
||||
return insertLock(lockName, overdueTime);
|
||||
} else {
|
||||
// 存在当前的锁的锁名称
|
||||
if (lockPojo.getLockStatus() == 1) {
|
||||
// 锁处于锁定状态
|
||||
return onLock(lockName, overdueTime, reentrant, lockPojo);
|
||||
} else {
|
||||
// 处于未锁定状态开始对资源上锁
|
||||
boolean lock = mapper.lock(lockPojo.getId(), lockName, overdueTime);
|
||||
if (lock) {
|
||||
// 锁定成功
|
||||
LOCK_MAP.put(lockName,
|
||||
Builder.builder(LockEntity::new)
|
||||
.with(LockEntity::setTimes, 1)
|
||||
.with(LockEntity::setLockMark, Thread.currentThread())
|
||||
.with(LockEntity::setExpirationTime, overdueTime)
|
||||
.build());
|
||||
return true;
|
||||
} else {
|
||||
// 抢占锁失败
|
||||
lockPojo = mapper.selectLock(lockName);
|
||||
return retryGetLock(lockName, overdueTime, lockPojo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>处于锁状态</h2>
|
||||
* <i>2022/12/11 12:23</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param lockName 锁名称
|
||||
* @param overdueTime 锁超时时间
|
||||
* @param reentrant 是否可重入锁
|
||||
* @param lockPojo 锁信息对象
|
||||
* @return boolean 是否获取锁成功
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
private static boolean onLock(String lockName, Long overdueTime, boolean reentrant, LockPojo lockPojo) {
|
||||
Long time = lockPojo.getExpirationTime();
|
||||
if (time > System.currentTimeMillis()) {
|
||||
// 锁过期了 如果要使锁生效就将日期加长
|
||||
return lockExpiration(lockName, overdueTime, lockPojo);
|
||||
} else {
|
||||
//锁没有过期
|
||||
return inLockTime(lockName, overdueTime, reentrant);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>锁未过期</h2>
|
||||
* <i>2022/12/11 12:24</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param lockName 锁名称
|
||||
* @param overdueTime 锁过期时间
|
||||
* @param reentrant 是否可重入
|
||||
* @return boolean 是否成功获取锁
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
private static boolean inLockTime(String lockName, Long overdueTime, boolean reentrant) {
|
||||
//判断是否存在本地锁对象中
|
||||
LockPojo lockPojo = mapper.selectLock(lockName);
|
||||
if (!LOCK_MAP.containsKey(lockName)) {
|
||||
// 不是本地锁,不支持可重入,使线程进入等待状态
|
||||
int n = 0;
|
||||
return retryGetLock(lockName, overdueTime, lockPojo);
|
||||
}
|
||||
// 是本地锁
|
||||
return getLock(lockName, overdueTime, reentrant);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>尝试抢占资源锁</h2>
|
||||
* <i>2022/12/11 15:34</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param lockName 锁名称
|
||||
* @param overdueTime 锁过期时间
|
||||
* @param lockPojo 锁参数对象
|
||||
* @return boolean 是否抢占锁成功
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
private static boolean retryGetLock(String lockName, Long overdueTime, LockPojo lockPojo) {
|
||||
while (lockPojo.getExpirationTime() > System.currentTimeMillis()) {
|
||||
// 锁还没过期,使线程休眠
|
||||
long l = lockPojo.getExpirationTime() - System.currentTimeMillis();
|
||||
if (l > 10) {
|
||||
ThreadUtil.safeSleep(l - 10L);
|
||||
}
|
||||
// 尝试获取锁
|
||||
lockExpiration(lockName, overdueTime + l, lockPojo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>获取锁</h2>
|
||||
* <i>2022/12/11 13:57</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param lockName 锁名称
|
||||
* @param overdueTime 超时时间
|
||||
* @param reentrant 是否可重入
|
||||
* @return boolean 锁是否获取成功
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
private static boolean getLock(String lockName, Long overdueTime, boolean reentrant) {
|
||||
if (reentrant) {
|
||||
LockEntity lockEntity = LOCK_MAP.get(lockName);
|
||||
lockEntity.setTimes(lockEntity.getTimes() + 1);
|
||||
} else {
|
||||
LockPojo lockPojo = mapper.selectLock(lockName);
|
||||
retryGetLock(lockName, overdueTime, lockPojo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>锁过期</h2>
|
||||
* <i>2022/12/11 12:25</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param lockName 锁名称
|
||||
* @param overdueTime 锁过期时间
|
||||
* @return boolean 是否成功获取锁
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
private static boolean lockExpiration(String lockName, Long overdueTime, LockPojo lockPojo) {
|
||||
boolean lock = mapper.reLock(lockPojo.getId(), lockName, overdueTime, lockPojo.getExpirationTime());
|
||||
if (lock) {
|
||||
//更新锁状态成功
|
||||
LockEntity lockEntity = Builder.builder(LockEntity::new)
|
||||
.with(LockEntity::setTimes, 1)
|
||||
.with(LockEntity::setLockMark, Thread.currentThread())
|
||||
.with(LockEntity::setExpirationTime, overdueTime)
|
||||
.build();
|
||||
LOCK_MAP.put(lockName, lockEntity);
|
||||
return true;
|
||||
} else {
|
||||
// 更新失败,可能是其他服务器抢先获得了锁
|
||||
LockPojo newLockPojo = mapper.selectLock(lockName);
|
||||
if (Objects.isNull(newLockPojo)) {
|
||||
// 锁被释放了
|
||||
boolean isLock = insertLock(lockName, overdueTime);
|
||||
if (!isLock) {
|
||||
throw new CustomerException("can not getLock!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (newLockPojo.getExpirationTime() > lockPojo.getExpirationTime()) {
|
||||
// 锁被其他机器抢占
|
||||
return retryGetLock(lockName, overdueTime, newLockPojo);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>新增锁记录</h2>
|
||||
* <i>2022/12/11 12:25</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param lockName 锁名称
|
||||
* @param overdueTime 锁过期时间
|
||||
* @return boolean 是否是可重入锁
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
private static boolean insertLock(String lockName, Long overdueTime) {
|
||||
// 没有锁需要新增一条锁记录
|
||||
String modeId = Util.getModeIdByTableName(UF_CUS_LOCK);
|
||||
int dataId = Util.getModeDataId(UF_CUS_LOCK, Integer.parseInt(modeId), 1);
|
||||
long currentTime = System.currentTimeMillis();
|
||||
Long expirationTime = currentTime + overdueTime;
|
||||
boolean lock = mapper.lock(dataId, lockName, expirationTime);
|
||||
if (lock) {
|
||||
// 锁成功
|
||||
LockEntity lockEntity = Builder.builder(LockEntity::new)
|
||||
.with(LockEntity::setTimes, 1)
|
||||
.with(LockEntity::setLockMark, Thread.currentThread())
|
||||
.with(LockEntity::setExpirationTime, overdueTime)
|
||||
.build();
|
||||
LOCK_MAP.put(lockName, lockEntity);
|
||||
} else {
|
||||
// 锁失败
|
||||
retryLock(dataId, lockName, expirationTime, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <h2>重试锁</h2>
|
||||
* <i>2022/12/11 01:39</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param dataId 数据id
|
||||
* @param lockName 锁名称
|
||||
* @param expirationTime 过期时间
|
||||
* @param n 重试次数
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
private static void retryLock(int dataId, String lockName, Long expirationTime, int n) {
|
||||
if (n > 5) {
|
||||
throw new CustomerException("get lock error! 5 failed attempts to update the database!");
|
||||
}
|
||||
ThreadUtil.safeSleep((n + 1) * 500);
|
||||
boolean lock = mapper.lock(dataId, lockName, expirationTime + (n + 1) * 500L);
|
||||
if (lock) {
|
||||
// 锁成功
|
||||
LockEntity lockEntity = Builder.builder(LockEntity::new)
|
||||
.with(LockEntity::setTimes, 1)
|
||||
.with(LockEntity::setLockMark, Thread.currentThread())
|
||||
.with(LockEntity::setExpirationTime, expirationTime + (1 + n) * 500L)
|
||||
.build();
|
||||
LOCK_MAP.put(lockName, lockEntity);
|
||||
} else {
|
||||
// 锁失败
|
||||
retryLock(dataId, lockName, expirationTime, n + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <h2>锁续期</h2>
|
||||
* <i>2022/12/11 15:49</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param lockKey 锁名字
|
||||
* @param expirationTime 锁过期时间
|
||||
* @return boolean 是否续期成功
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
public boolean updateLockTime(String lockKey, Long expirationTime) {
|
||||
if (!mapper.updateLockTime(lockKey, expirationTime)) {
|
||||
// 更新失败
|
||||
int n = 0;
|
||||
do {
|
||||
if (n++ > 5) {
|
||||
return false;
|
||||
}
|
||||
} while (!mapper.updateLockTime(lockKey, expirationTime));
|
||||
}
|
||||
if (LOCK_MAP.containsKey(lockKey)) {
|
||||
// 存在本地锁,更新锁信息
|
||||
LockEntity lockEntity = LOCK_MAP.get(lockKey);
|
||||
lockEntity.setExpirationTime(expirationTime);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <h2>释放锁</h2>
|
||||
* <i>2022/12/11 15:56</i>
|
||||
* ******************************************
|
||||
*
|
||||
* @param lockName 锁名称
|
||||
* @author youHong.ai ******************************************
|
||||
*/
|
||||
public void unLock(String lockName) {
|
||||
if (LOCK_MAP.containsKey(lockName)) {
|
||||
// 存在本地锁
|
||||
LockEntity lockEntity = LOCK_MAP.get(lockName);
|
||||
if (!lockEntity.getLockMark().equals(Thread.currentThread())) {
|
||||
// 并非当前上锁的线程在释放锁
|
||||
return;
|
||||
}
|
||||
Integer times = lockEntity.getTimes();
|
||||
if (times - 1 == 0) {
|
||||
boolean unlock = mapper.unLock(lockName);
|
||||
if (!unlock) {
|
||||
int n = 0;
|
||||
do {
|
||||
if (n++ > 5) {
|
||||
throw new CustomerException("can not unLock!Failed to release the lock after five attempts!");
|
||||
}
|
||||
} while (!mapper.unLock(lockName)); // 释放锁失败
|
||||
}
|
||||
return;
|
||||
}
|
||||
lockEntity.setTimes(lockEntity.getTimes() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -380,7 +380,7 @@ public class DealWithMapping extends ToolUtil {
|
|||
List<Object> tempList = new ArrayList();
|
||||
for (Object o : list) {
|
||||
if (o instanceof Map) {
|
||||
// map处理 key->1的类型
|
||||
// map处理 key->1的类型
|
||||
Map<String, Object> mapItem = (Map<String, Object>) o;
|
||||
Map<String, List<ListMapIndexValue>> tempMap = new HashMap<>(8);
|
||||
for (Map.Entry<String, Object> entry : mapItem.entrySet()) {
|
||||
|
@ -426,11 +426,11 @@ public class DealWithMapping extends ToolUtil {
|
|||
}
|
||||
}
|
||||
}
|
||||
// tempList.addAll(list);
|
||||
// tempList.addAll(list);
|
||||
requestParam.put(paramName, tempList);
|
||||
// if (!tempList.isEmpty()) {
|
||||
// requestParam.put(paramName, tempList);
|
||||
// }
|
||||
// if (!tempList.isEmpty()) {
|
||||
// requestParam.put(paramName, tempList);
|
||||
// }
|
||||
} else {
|
||||
Object value = this.normalValueDeal(mainMap, detailMap, mappingDetail);
|
||||
if (!Objects.isNull(value)) {
|
||||
|
@ -471,7 +471,7 @@ public class DealWithMapping extends ToolUtil {
|
|||
List<Object> tempList = new ArrayList();
|
||||
for (Object o : list) {
|
||||
if (o instanceof Map) {
|
||||
// map处理
|
||||
// map处理
|
||||
Map<String, Object> mapItem = (Map<String, Object>) o;
|
||||
Map<String, List<ListMapIndexValue>> tempMap = new HashMap<>(8);
|
||||
for (Map.Entry<String, Object> entry : mapItem.entrySet()) {
|
||||
|
@ -517,11 +517,11 @@ public class DealWithMapping extends ToolUtil {
|
|||
}
|
||||
}
|
||||
}
|
||||
// tempList.addAll(list);
|
||||
// tempList.addAll(list);
|
||||
requestParam.put(paramName, tempList);
|
||||
// if (!tempList.isEmpty()) {
|
||||
// requestParam.put(paramName, tempList);
|
||||
// }
|
||||
// if (!tempList.isEmpty()) {
|
||||
// requestParam.put(paramName, tempList);
|
||||
// }
|
||||
} else {
|
||||
Object value = this.normalValueDeal(mainMap, detailMap, relationRs, mappingDetail);
|
||||
if (!Objects.isNull(value)) {
|
||||
|
@ -618,7 +618,7 @@ public class DealWithMapping extends ToolUtil {
|
|||
} else {
|
||||
value = Util.null2String(mainMap.get("id"));
|
||||
}
|
||||
// value = Util.null2String(main.getString("id"));
|
||||
// value = Util.null2String(main.getString("id"));
|
||||
}
|
||||
break;
|
||||
// 随机数
|
||||
|
@ -657,7 +657,7 @@ public class DealWithMapping extends ToolUtil {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
// 自定义接口
|
||||
// 自定义接口
|
||||
case CUS_INTERFACE: {
|
||||
if (null == valueContext || valueContext.length() == 0) {
|
||||
} else {
|
||||
|
@ -943,7 +943,7 @@ public class DealWithMapping extends ToolUtil {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
// 自定义接口
|
||||
// 自定义接口
|
||||
case CUS_INTERFACE: {
|
||||
if (null == valueContext || valueContext.length() == 0) {
|
||||
} else {
|
||||
|
@ -1124,10 +1124,10 @@ public class DealWithMapping extends ToolUtil {
|
|||
list.add(o);
|
||||
}
|
||||
}
|
||||
// for (MappingDetail mappingDetail : childList) {
|
||||
// Object o = this.normalValueDeal(recordSet, null, mappingDetail);
|
||||
// list.add(o);
|
||||
// }
|
||||
// for (MappingDetail mappingDetail : childList) {
|
||||
// Object o = this.normalValueDeal(recordSet, null, mappingDetail);
|
||||
// list.add(o);
|
||||
// }
|
||||
} else if ("2".equals(childSource)) {
|
||||
// 自定义接口
|
||||
Map<String, String> pathParamMap = new HashMap<>(8);
|
||||
|
@ -1203,10 +1203,10 @@ public class DealWithMapping extends ToolUtil {
|
|||
list.add(o);
|
||||
}
|
||||
}
|
||||
// for (MappingDetail mappingDetail : childList) {
|
||||
// Object o = this.normalValueDeal(recordSet, null, mappingDetail);
|
||||
// list.add(o);
|
||||
// }
|
||||
// for (MappingDetail mappingDetail : childList) {
|
||||
// Object o = this.normalValueDeal(recordSet, null, mappingDetail);
|
||||
// list.add(o);
|
||||
// }
|
||||
} else if ("2".equals(childSource)) {
|
||||
// 自定义接口
|
||||
Map<String, String> pathParamMap = new HashMap<>(8);
|
||||
|
@ -1241,8 +1241,9 @@ public class DealWithMapping extends ToolUtil {
|
|||
|
||||
/**
|
||||
* <h2>请求结果赋值到配置信息中</h2>
|
||||
*
|
||||
* @param responseMappingList 回写信息配置列表
|
||||
* @param requestRes 请求结果
|
||||
* @param requestRes 请求结果
|
||||
* @return 要插入的信息
|
||||
*/
|
||||
public Map<String, Map<String, Object>> dealResponse(List<ResponseMapping> responseMappingList, Map<String, Object> requestRes) {
|
||||
|
@ -1374,9 +1375,9 @@ public class DealWithMapping extends ToolUtil {
|
|||
value:#sql{select workcode from hrmresource where id = #{main.zd1} and test = #{h-hah} and a in (${hrmids})}
|
||||
key:hah
|
||||
value:haode*/
|
||||
// 最终通过反射调用weaver.aiyh_jitu.pushdata.service.GetAssignProcessorProcessorImpl类,将参数传递给这个类, 使用``包裹的字符串会被解析为一个字符串
|
||||
// 最终通过反射调用weaver.aiyh_jitu.pushdata.service.GetAssignProcessorProcessorImpl类,将参数传递给这个类, 使用``包裹的字符串会被解析为一个字符串
|
||||
String pattern = "&?(?<key>([#.\\w\\u4E00-\\u9FA5]+))=" +
|
||||
"(?<value>((`([():/\\-&$?#={ }.\\w\\u4E00-\\u9FA5?]*)`)|" +
|
||||
"(?<value>((`([^`]*)`)|" +
|
||||
"((#(\\{|sql\\{))?([():/\\-$#={ }.\\w\\u4E00-\\u9FA5?]+)?}?)))&?";
|
||||
Pattern compile = Pattern.compile(pattern);
|
||||
Matcher matcher = compile.matcher(paramStr);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package youhong.ai.pcn;
|
||||
|
||||
import aiyh.utils.Util;
|
||||
import aiyh.utils.httpUtil.HttpMultipartFile;
|
||||
import aiyh.utils.httpUtil.ResponeVo;
|
||||
import aiyh.utils.httpUtil.util.HttpUtils;
|
||||
|
@ -108,6 +109,21 @@ public class TestOrganization extends BaseTest {
|
|||
@Test
|
||||
public void testStaticLog() {
|
||||
log.info("哈哈哈好的方式");
|
||||
String testStr = "slflas.fasjdflaf.fasdf?hah=liuliu&cus=`select? * fr$%&#@!)(<>?/\\{}「」【【】[]~、asfom table where id = '' and teset = #{name}`&niua=卧槽";
|
||||
Map<String, String> stringStringMap = Util.parseCusInterfacePathParam(testStr);
|
||||
System.out.println(JSON.toJSONString(stringStringMap));
|
||||
//String pattern = "&?(?<key>([#.\\w\\u4E00-\\u9FA5]+))=" +
|
||||
// "(?<value>(`([():/\\-&$#='*{ }.\\w\\u4E00-\\u9FA5?]*)`|((#(\\{|sql\\{))?([():/\\-$#={ }.\\w\\u4E00-\\u9FA5?]+)?}?)))&?";
|
||||
//String pattern = "=`(?<value>(([\\u4E00-\\u9FA5\\w.'#=?${ }*])*))`";
|
||||
//String pattern = "&?(?<key>([#.\\w\\u4E00-\\u9FA5]+))=(?<value>(`([():/\\-&?%#\\t\\n='{ }.\\w\\u4E00-\\u9FA5]*)*`))";
|
||||
//Pattern compile = Pattern.compile(pattern);
|
||||
//Matcher matcher = compile.matcher(testStr);
|
||||
//while (matcher.find()) {
|
||||
// String key = matcher.group("key");
|
||||
// System.out.println(key);
|
||||
//String paramValue = matcher.group("value");
|
||||
//System.out.println(paramValue);
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue