增加定时器功能

Conflicts:
	src/main/webapp/WEB-INF/views/cfg/ipaddr/ipList.jsp
This commit is contained in:
zhangwei
2019-01-26 18:02:44 +06:00
parent 5c63f85483
commit 9d6e3a3d4a
41 changed files with 5184 additions and 335 deletions

View File

@@ -0,0 +1,93 @@
package com.nis.persistence.interceptor;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.log4j.Logger;
import com.nis.domain.ScheduleCfg;
import com.nis.domain.configuration.BaseCfg;
import com.nis.util.ServiceConfigTemplateUtil;
/**
* 定时任务 拦截器
* 1、拦截 select 操作
* 2、判断是否 form 查询配置主表
* 3、如果是配置主表查询对应的 scheduleCfg信息
* @author fang
*
*/
@Intercepts({@Signature(type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class ScheduleSelectInterceptor extends BaseInterceptor{
private static final long serialVersionUID = -265785145929843646L;
private static final Logger logger = Logger.getLogger(ScheduleSelectInterceptor.class);
@Override
@SuppressWarnings("all")
public Object intercept(Invocation invocation) throws Throwable {
final MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
final Executor executor = (Executor)invocation.getTarget();
// select sql 查询结果
Object result = invocation.proceed();
if(result != null ) {
if(result instanceof Collection) {
Collection c = (Collection)result;
// if(c.size()==1) {//如果只查询一个 配置可以判定为是 form update 页面查询
if(c.size() >0) {//配置列表需要查询是否为定时任务,所以需要遍历
Iterator iterator = c.iterator();
while(iterator.hasNext()){
Object next = iterator.next();
if(next instanceof BaseCfg<?>) {
BaseCfg cfg = (BaseCfg)next;
//结果集的类名
String simpleName = next.getClass().getSimpleName();
//根据类名查找对应的 表名
String tableName = ServiceConfigTemplateUtil.getCfgTableNameByClassName(simpleName);
if(tableName != null) {//是配置主表
Configuration configuration = mappedStatement.getConfiguration();
MappedStatement scheduleStmt = configuration.getMappedStatement("com.nis.web.dao.SchedulerDao.findScheduleList");
ScheduleCfg sc = new ScheduleCfg();
sc.setTableName(tableName);
sc.setCfgId(cfg.getCfgId());
sc.setCompileId(cfg.getCompileId());
//executor.clearLocalCache();
List<ScheduleCfg> schedules = executor.query(scheduleStmt, sc, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
if(schedules != null&& schedules.size() > 0) {
cfg.setSchedule(schedules.get(0));
}
logger.debug(String.format("配置bean中赋值scheduleCfg,表名:%s,类名:%s,cfgId:%s,compileId:%s,schedules num:%s", tableName,simpleName,sc.getCfgId(),sc.getCompileId(),schedules==null?0:schedules.size()));
}
}else{
break;
}
}
}
}
}
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}

View File

@@ -0,0 +1,389 @@
package com.nis.persistence.interceptor;
import java.lang.reflect.Array;
import java.sql.SQLException;
import java.text.DateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.google.common.collect.Lists;
import com.nis.domain.ScheduleCfg;
import com.nis.domain.ScheduleExceInfo;
import com.nis.domain.configuration.BaseCfg;
import com.nis.util.ServiceConfigTemplateUtil;
import com.nis.web.security.UserUtils;
import jersey.repackaged.com.google.common.collect.Sets;
/**
* 定时任务 拦截器
* 1、拦截insert,update,delete操作
* 2、判断是否是需要下发配置的表
* 3、如果是需要下发配置的表则同时将数据保存到schedule_cfg表中并调用定时任务
* @author fang
*
*/
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ScheduleUpdateInterceptor extends BaseInterceptor{
private static final long serialVersionUID = -265785145929843646L;
private static final Logger logger = Logger.getLogger(ScheduleUpdateInterceptor.class);
private static final String TABLE_NAME_INSERT_REG = "^\\s*insert\\s+into\\s+([\\w_-]+)\\s*\\(";
private static final Pattern TABLE_NAME_INSERT_PATTERN = Pattern.compile(TABLE_NAME_INSERT_REG, Pattern.CASE_INSENSITIVE);//大小写
private static final String TABLE_NAME_UPDATE_REG = "^\\s*update\\s+([\\w_-]+)";
private static final Pattern TABLE_NAME_UPDATE_PATTERN = Pattern.compile(TABLE_NAME_UPDATE_REG, Pattern.CASE_INSENSITIVE);//大小写
private static final String TABLE_NAME_DELETE_REG = "^\\s*delete\\s+from\\s+([\\w_-]+)";
private static final Pattern TABLE_NAME_DELETE_PATTERN = Pattern.compile(TABLE_NAME_DELETE_REG, Pattern.CASE_INSENSITIVE);//大小写
/**
* 排除拦截的id
*/
private static final Set<String> EXCLUDE_MAPPER_IDS = Sets.newHashSet();
static {
EXCLUDE_MAPPER_IDS.add("com.nis.web.dao.SchedulerDao.updateCfgTableStatus");
EXCLUDE_MAPPER_IDS.add("com.nis.web.dao.basics.ServiceDictInfoDao");
}
/**
* is_valid 字段名
*/
private static final String IS_VALID_COLUMN = "is_valid";
/**
* is_audit 字段名
*/
private static final String IS_AUDIT_COLUMN = "is_audit";
@Override
@SuppressWarnings("all")
public Object intercept(Invocation invocation) throws Throwable {
final MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
final Executor executor = (Executor)invocation.getTarget();
Object parameter = invocation.getArgs()[1];//被拦截的 sql 参数
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
Object result = invocation.proceed();//被拦截的 sql 操作 继续执行
String curProceedId = mappedStatement.getId();
//不需要拦截的操作直接返回结果
if(EXCLUDE_MAPPER_IDS.contains(curProceedId)) {
return result;
}
String tableName = null;//操作的表名
//mybatis 整个mapper 信息
Configuration configuration = mappedStatement.getConfiguration();
//被拦截的 sql语句
String sql = boundSql.getSql();
//所有需要拦截的表名
Set<String> tableNames = ServiceConfigTemplateUtil.getCompileTableName();
switch (mappedStatement.getSqlCommandType()) {
case INSERT://新增配置
Matcher insertMatcher = TABLE_NAME_INSERT_PATTERN.matcher(sql);
tableName = insertMatcher.find()?insertMatcher.group(1):tableName;
if(tableName != null && tableNames.contains(tableName.toLowerCase())) {
insertCfg(executor,parameter, configuration,tableName);
}
break;
case UPDATE://更新配置
Matcher updateMatcher = TABLE_NAME_UPDATE_PATTERN.matcher(sql);
tableName = updateMatcher.find()?updateMatcher.group(1):tableName;
if(tableName != null && tableNames.contains(tableName.toLowerCase())) {
updateCfg(executor,boundSql, configuration,tableName);
}
break;
case DELETE://删除配置
Matcher deleteMatcher = TABLE_NAME_DELETE_PATTERN.matcher(sql);
tableName = deleteMatcher.find()?deleteMatcher.group(1):tableName;
if(tableName != null && tableNames.contains(tableName.toLowerCase())) {
deleteCfg(executor,parameter, configuration,tableName);
}
break;
default:
break;
}
logger.debug(sql);
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
/**
* @throws SQLException
*
*/
@SuppressWarnings("all")
private void insertCfg(Executor executor,Object parameterObject,Configuration configuration,String tableName) throws SQLException {
List<BaseCfg> cfgList = Lists.newArrayList();
//确保 单个,批量都适用
if(parameterObject instanceof BaseCfg<?>) {//单个添加
BaseCfg<?> baseCfg = (BaseCfg<?>)parameterObject;
cfgList.add(baseCfg);
}else if(parameterObject instanceof Collection) {
Collection<BaseCfg<?>> bcCollection = (Collection<BaseCfg<?>>)parameterObject;
cfgList.addAll(bcCollection);
}else if(parameterObject.getClass().isArray()) {
int length = Array.getLength(parameterObject);
for(int i = 0;i< length;i++) {
BaseCfg<?> baseCfg = (BaseCfg<?>)Array.get(parameterObject, i);
cfgList.add(baseCfg);
}
}
//整理需要 insert 的 schedule_cfg 的数据
List<ScheduleCfg> scheduleList = Lists.newArrayList();
for(BaseCfg<?> baseCfg : cfgList) {
baseCfg.setIsValid(0);//设置默认值
baseCfg.setIsAudit(0);//设置默认值
ScheduleCfg scList = copyScheduleCfgFromBaseCfg(baseCfg, tableName);
if(scList!=null){
scheduleList.add(scList);
}
}
if(scheduleList.size() > 0) {
for(ScheduleCfg sc : scheduleList) {
MappedStatement statement = configuration.getMappedStatement("com.nis.web.dao.SchedulerDao.insert");
executor.update(statement, sc);
}
}
}
/**
*
*/
@SuppressWarnings("all")
private void updateCfg(Executor executor,BoundSql boundSql,Configuration configuration,String tableName) throws SQLException {
/*
* 条件1 is_valid=0,is_audit=0; 未生效之前修改,来自修改页面
*1、首先将所有相关定时任务信息取消
*2、 新增 定时任务信息
*/
Object parameterObject = boundSql.getParameterObject();
if(parameterObject instanceof BaseCfg) {
BaseCfg bc = (BaseCfg)parameterObject;
Integer isValid = bc.getIsValid();
Integer isAudit = bc.getIsAudit();
Integer compileId = bc.getCompileId();
Long cfgId = bc.getCfgId();
if(isValid == 0 && isAudit == 0) {
ScheduleCfg sc = new ScheduleCfg();
sc.setCompileId(compileId);
sc.setEditorId(bc.getEditorId());
sc.setEditTime(bc.getEditTime());
sc.setCfgId(cfgId);
//根据 compileId 删除之前的
MappedStatement statement = configuration.getMappedStatement("com.nis.web.dao.SchedulerDao.deleteByCompileId");
executor.update(statement, sc);
//新增 更改的
ScheduleCfg schedule = copyScheduleCfgFromBaseCfg(bc, tableName);
if(schedule != null ) {
statement = configuration.getMappedStatement("com.nis.web.dao.SchedulerDao.insert");
executor.update(statement, schedule);
//配置信息新增,或取消之后重新修改,需要修改 exce_new表的状态
statement = configuration.getMappedStatement("com.nis.web.dao.SchedulerDao.updateScheduleExceNew");
ScheduleExceInfo exceNew = new ScheduleExceInfo();
exceNew.setCompileId(compileId);
exceNew.setIssueStatus(1);
exceNew.setIsIssue(1);//定时任务执行时需要下发配置
executor.update(statement, exceNew);
}
return;
}
}
//完整的sql语句已经完成 ? 替换
String sql = showSql(configuration, boundSql);
log.debug(String.format("cfg update sql : " , sql));
//其它剩余基本判定为 修改状态的 update
MySqlStatementParser parser = new MySqlStatementParser(sql);
MySqlUpdateStatement updateSqlStmt = (MySqlUpdateStatement)parser.parseStatement();
List<SQLUpdateSetItem> items = updateSqlStmt.getItems();
/* 分别获取 is_audit & is_valid 的值*/
Integer isValid = null;
Integer isAudit = null;
for(SQLUpdateSetItem item : items) {
if(IS_VALID_COLUMN.equalsIgnoreCase(item.getColumn().toString())) {
isValid = Integer.valueOf(item.getValue().toString());
continue;
}
if(IS_AUDIT_COLUMN.equalsIgnoreCase(item.getColumn().toString())) {
isAudit = Integer.valueOf(item.getValue().toString());
continue;
}
if(isValid != null && isAudit != null) {
break;
}
}
//isValid 和 isAudit 同时为空,目前发现有 取消来函的 update 操作有此情况
if(isValid == null && isAudit == null) {
return;
}
//获取当前的update 条件
String whereStr = updateSqlStmt.getWhere().toString();
StringBuilder whereSb = new StringBuilder();
whereSb.append(" and table_name = '");
whereSb.append(tableName);
whereSb.append("' and ( ");
whereSb.append(whereStr);
whereSb.append(" )");
//根据当前的 update 条件 查出所有符合条件的 schedule_cfg 数据
MappedStatement scheduleStmt = configuration.getMappedStatement("com.nis.web.dao.SchedulerDao.findScheduleList");
ScheduleCfg sc = new ScheduleCfg();
sc.setWhereStr(whereSb.toString());
List<ScheduleCfg> schedules = executor.query(scheduleStmt, sc, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
if(schedules != null && schedules.size() > 0) {//有符合条件的 定时任务
Set<Integer> compileSet = Sets.newHashSet();//保存操作的所有 compileId
Date now = new Date();//当前时间
//1、将符合条件的是 ScheduleCfg update del_flag = 0
for(ScheduleCfg scfg : schedules) {
compileSet.add(scfg.getCompileId());
scfg.setEditorId(UserUtils.getUser().getId());
scfg.setEditTime(now);
MappedStatement statement = configuration.getMappedStatement("com.nis.web.dao.SchedulerDao.deleteByCompileId");
executor.update(statement, scfg);
}
//2、把原信息 重新保存,并修改 is_valid ,is_audit 状态
for(ScheduleCfg scfg : schedules) {
MappedStatement statement = configuration.getMappedStatement("com.nis.web.dao.SchedulerDao.insert");
scfg.setIsValid(isValid);
scfg.setIsAudit(isAudit);
executor.update(statement, scfg);
}
//手动 审核通过,立即生效时 已经下发,修改 exce_new 表的是否需要下发字段为 不需要 0
if(isValid == 1 && isAudit == 1) {
for(Integer ci : compileSet) {
MappedStatement statement = configuration.getMappedStatement("com.nis.web.dao.SchedulerDao.findScheduleExceNew");
ScheduleExceInfo se = new ScheduleExceInfo();
se.setCompileId(ci);
se.setIsValid(isValid);
List<ScheduleExceInfo> seList = executor.query(statement, se, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
if(seList == null || seList.size() < 1) {//没有执行记录,新增一条
//配置信息新增,或取消之后重新修改,需要修改 exce_new表的状态
statement = configuration.getMappedStatement("com.nis.web.dao.SchedulerDao.insertScheduleExceNew");
ScheduleExceInfo exceNew = new ScheduleExceInfo();
exceNew.setCompileId(ci);
exceNew.setIssueStatus(1);
exceNew.setIsIssue(0);//已经下发,定时任务不需要下发
exceNew.setExceTime(new Date());
executor.update(statement, exceNew);
}else {
//配置信息新增,或取消之后重新修改,需要修改 exce_new表的状态
statement = configuration.getMappedStatement("com.nis.web.dao.SchedulerDao.updateScheduleExceNew");
ScheduleExceInfo exceNew = new ScheduleExceInfo();
exceNew.setCompileId(ci);
exceNew.setIssueStatus(1);
exceNew.setIsIssue(0);//已经下发,定时任务不需要下发
executor.update(statement, exceNew);
}
}
}
}
}
/**
* 暂时没有发现有 删除操作
*/
@SuppressWarnings("all")
private void deleteCfg(Executor executor,Object parameterObject,Configuration configuration,String tableName) throws SQLException {
}
/**
* 从 basecfg 实体类中获取 schedule cfg
* @param baseCfg
* @param tableName
* @return
*/
private ScheduleCfg copyScheduleCfgFromBaseCfg(BaseCfg<?> baseCfg,String tableName){
ScheduleCfg schedule = baseCfg.getSchedule();
if(schedule != null ) {
BeanUtils.copyProperties(baseCfg, schedule,new String[]{"userRegion1","userRegion2","userRegion3","userRegion4","userRegion5"});
schedule.setTableName(tableName);
}
return schedule;
}
/*如果参数是String则添加单引号 如果是日期,则转换为时间格式器并加单引号; 对参数是null和不是null的情况作了处理*/
private static String getParameterValue(Object obj) {
String value = null;
if (obj instanceof String) {
value = "'" + obj.toString() + "'";
} else if (obj instanceof Date) {
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.getDefault());
value = "'" + formatter.format(new Date()) + "'";
} else {
if (obj != null) {
value = obj.toString();
} else {
value = "null";
}
}
return value;
}
// 进行?的替换
public static String showSql(Configuration configuration, BoundSql boundSql) {
Object parameterObject = boundSql.getParameterObject(); // 获取参数
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
String sql = boundSql.getSql().replaceAll("[\\s]+", " "); // sql语句中多个空格都用一个空格代替
if (parameterMappings != null && parameterMappings.size() >0 && parameterObject != null) {
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); // 获取类型处理器注册器类型处理器的功能是进行java类型和数据库类型的转换<br>       // 如果根据parameterObject.getClass()可以找到对应的类型,则替换
if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);// MetaObject主要是封装了originalObject对象提供了get和set的方法用于获取和设置originalObject的属性值,主要支持对JavaBean、Collection、Map三种类型对象的操作
for (ParameterMapping parameterMapping : parameterMappings) {
String propertyName = parameterMapping.getProperty();
if (metaObject.hasGetter(propertyName)) {
Object obj = metaObject.getValue(propertyName);
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
} else if (boundSql.hasAdditionalParameter(propertyName)) {
Object obj = boundSql.getAdditionalParameter(propertyName); // 该分支是动态sql
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
}else{sql=sql.replaceFirst("\\?","缺失");}//打印出缺失,提醒该参数缺失并防止错位
}
}
}
return sql;
}
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
}
}