001/*
002 *  Copyright (c) 2022-2024, Mybatis-Flex (fuhai999@gmail.com).
003 *  <p>
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.mybatisflex.core.audit;
017
018import com.mybatisflex.core.FlexConsts;
019import com.mybatisflex.core.FlexGlobalConfig;
020import com.mybatisflex.core.datasource.DataSourceKey;
021import com.mybatisflex.core.util.CollectionUtil;
022import com.mybatisflex.core.util.StringUtil;
023import org.apache.ibatis.mapping.BoundSql;
024import org.apache.ibatis.mapping.ParameterMapping;
025import org.apache.ibatis.mapping.ParameterMode;
026import org.apache.ibatis.reflection.MetaObject;
027import org.apache.ibatis.session.Configuration;
028import org.apache.ibatis.type.TypeHandlerRegistry;
029
030import java.sql.SQLException;
031import java.sql.Statement;
032import java.util.Collection;
033import java.util.List;
034import java.util.Map;
035
036/**
037 * 审计管理器,统一执行如何和配置入口
038 */
039public class AuditManager {
040
041
042    private AuditManager() {
043    }
044
045
046    private static MessageFactory messageFactory = new DefaultMessageFactory();
047
048    private static boolean auditEnable = false;
049    private static Clock clock = System::currentTimeMillis;
050    private static MessageCollector messageCollector = new ScheduledMessageCollector();
051
052    public static boolean isAuditEnable() {
053        return auditEnable;
054    }
055
056    public static void setAuditEnable(boolean auditEnable) {
057        AuditManager.auditEnable = auditEnable;
058    }
059
060    public static Clock getClock() {
061        return clock;
062    }
063
064    public static void setClock(Clock clock) {
065        AuditManager.clock = clock;
066    }
067
068    public static MessageFactory getMessageFactory() {
069        return messageFactory;
070    }
071
072    public static void setMessageFactory(MessageFactory messageFactory) {
073        AuditManager.messageFactory = messageFactory;
074    }
075
076    public static MessageCollector getMessageCollector() {
077        return messageCollector;
078    }
079
080
081    public static void setMessageReporter(MessageReporter messageReporter) {
082        MessageCollector newMessageCollector = new ScheduledMessageCollector(10, messageReporter);
083        setMessageCollector(newMessageCollector);
084    }
085
086    public static void setMessageCollector(MessageCollector messageCollector) {
087        MessageCollector temp = AuditManager.messageCollector;
088        AuditManager.messageCollector = messageCollector;
089        releaseScheduledMessageCollector(temp);
090
091    }
092
093    private static void releaseScheduledMessageCollector(MessageCollector messageCollector) {
094        if (messageCollector instanceof ScheduledMessageCollector) {
095            ((ScheduledMessageCollector) messageCollector).release();
096        }
097    }
098
099    @SuppressWarnings("rawtypes")
100    public static <T> T startAudit(AuditRunnable<T> supplier, Statement statement, BoundSql boundSql, Configuration configuration) throws SQLException {
101        AuditMessage auditMessage = messageFactory.create();
102        if (auditMessage == null) {
103            return supplier.execute();
104        }
105        String key = DataSourceKey.get();
106        if (StringUtil.isBlank(key)) {
107            key = FlexGlobalConfig.getDefaultConfig()
108                .getDataSource()
109                .getDefaultDataSourceKey();
110        }
111        auditMessage.setDsName(key);
112        auditMessage.setQueryTime(clock.getTick());
113        try {
114            T result = supplier.execute();
115            if (result instanceof Collection) {
116                auditMessage.setQueryCount(((Collection) result).size());
117            } else if (result instanceof Number) {
118                auditMessage.setQueryCount(((Number) result).intValue());
119            } else if (result != null) {
120                auditMessage.setQueryCount(1);
121            }
122            return result;
123        } finally {
124            auditMessage.setElapsedTime(clock.getTick() - auditMessage.getQueryTime());
125            auditMessage.setQuery(boundSql.getSql());
126
127            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
128            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
129            Object parameter = boundSql.getParameterObject();
130
131            // 实现 XML 与 QueryWrapper 参数解析互不干涉
132
133            if (CollectionUtil.isNotEmpty(parameterMappings)) {
134                // 组装 XML 中的 #{user.age} 参数
135                for (ParameterMapping parameterMapping : parameterMappings) {
136                    if (parameterMapping.getMode() != ParameterMode.OUT) {
137                        Object value;
138                        String propertyName = parameterMapping.getProperty();
139                        if (boundSql.hasAdditionalParameter(propertyName)) {
140                            value = boundSql.getAdditionalParameter(propertyName);
141                        } else if (typeHandlerRegistry.hasTypeHandler(parameter.getClass())) {
142                            value = parameter;
143                        } else {
144                            MetaObject metaObject = configuration.newMetaObject(parameter);
145                            value = metaObject.getValue(propertyName);
146                        }
147                        auditMessage.addParams(statement, value);
148                    }
149                }
150            } else {
151                // 组装 QueryWrapper 里面的 age = ? 参数
152                // parameter 的组装请查看 ParamNameResolver#getNamedParams(Object[]) 方法
153                if (parameter instanceof Map) {
154                    Map<?, ?> map = (Map<?, ?>) parameter;
155                    if (map.containsKey(FlexConsts.SQL_ARGS)) {
156                        auditMessage.addParams(statement, map.get(FlexConsts.SQL_ARGS));
157                    } else if (map.containsKey("collection")) {
158                        Collection collection = (Collection) map.get("collection");
159                        auditMessage.addParams(statement, collection.toArray());
160                    } else if (map.containsKey("array")) {
161                        auditMessage.addParams(statement, map.get("array"));
162                    }
163                }
164            }
165
166            messageCollector.collect(auditMessage);
167        }
168    }
169
170
171    @FunctionalInterface
172    public interface AuditRunnable<T> {
173        T execute() throws SQLException;
174    }
175
176}