001/**
002 * Copyright (c) 2022-2023, 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 org.apache.ibatis.mapping.BoundSql;
020import org.apache.ibatis.mapping.ParameterMapping;
021import org.apache.ibatis.mapping.ParameterMode;
022import org.apache.ibatis.reflection.MetaObject;
023import org.apache.ibatis.reflection.ParamNameResolver;
024import org.apache.ibatis.session.Configuration;
025import org.apache.ibatis.type.TypeHandlerRegistry;
026
027import java.sql.SQLException;
028import java.util.Collection;
029import java.util.List;
030import java.util.Map;
031
032/**
033 * 审计管理器,统一执行如何和配置入口
034 */
035public class AuditManager {
036
037    private static boolean auditEnable = false;
038    private static Clock clock = System::currentTimeMillis;
039    private static MessageFactory MessageFactory = new DefaultMessageFactory();
040    private static MessageCollector messageCollector = new ScheduledMessageCollector();
041
042    public static boolean isAuditEnable() {
043        return auditEnable;
044    }
045
046    public static void setAuditEnable(boolean auditEnable) {
047        AuditManager.auditEnable = auditEnable;
048    }
049
050    public static Clock getClock() {
051        return clock;
052    }
053
054    public static void setClock(Clock clock) {
055        AuditManager.clock = clock;
056    }
057
058    public static MessageFactory getMessageFactory() {
059        return MessageFactory;
060    }
061
062    public static void setMessageFactory(MessageFactory MessageFactory) {
063        AuditManager.MessageFactory = MessageFactory;
064    }
065
066    public static MessageCollector getMessageCollector() {
067        return messageCollector;
068    }
069
070
071    public static void setMessageReporter(MessageReporter messageReporter) {
072        MessageCollector newMessageCollector = new ScheduledMessageCollector(10, messageReporter);
073        setMessageCollector(newMessageCollector);
074    }
075
076    public static void setMessageCollector(MessageCollector messageCollector) {
077        MessageCollector temp = AuditManager.messageCollector;
078        AuditManager.messageCollector = messageCollector;
079        releaseScheduledMessageCollector(temp);
080
081    }
082
083    private static void releaseScheduledMessageCollector(MessageCollector messageCollector) {
084        if (messageCollector instanceof ScheduledMessageCollector) {
085            ((ScheduledMessageCollector) messageCollector).release();
086        }
087    }
088
089    public static <T> T startAudit(AuditRunnable<T> supplier, BoundSql boundSql, Configuration configuration) throws SQLException {
090        AuditMessage auditMessage = MessageFactory.create();
091        if (auditMessage == null) {
092            return supplier.execute();
093        }
094        auditMessage.setQueryTime(clock.getTick());
095        try {
096            T result = supplier.execute();
097            if (result instanceof Collection) {
098                auditMessage.setQueryCount(((Collection) result).size());
099            } else if (result != null) {
100                auditMessage.setQueryCount(1);
101            }
102            return result;
103        } finally {
104            auditMessage.setElapsedTime(clock.getTick() - auditMessage.getQueryTime());
105            auditMessage.setQuery(boundSql.getSql());
106            Object parameter = boundSql.getParameterObject();
107
108            /** parameter 的组装请查看 getNamedParams 方法
109             * @see ParamNameResolver#getNamedParams(Object[])
110             */
111            if (parameter instanceof Map) {
112                TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
113                if (((Map<?, ?>) parameter).containsKey(FlexConsts.SQL_ARGS)) {
114                    auditMessage.addParams(((Map<?, ?>) parameter).get(FlexConsts.SQL_ARGS));
115                } else if (((Map<?, ?>) parameter).containsKey("collection")) {
116                    Collection collection = (Collection) ((Map<?, ?>) parameter).get("collection");
117                    auditMessage.addParams(collection.toArray());
118                } else if (((Map<?, ?>) parameter).containsKey("array")) {
119                    auditMessage.addParams(((Map<?, ?>) parameter).get("array"));
120                } else {
121                    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
122                    for (ParameterMapping parameterMapping : parameterMappings) {
123                        if (parameterMapping.getMode() != ParameterMode.OUT) {
124                            Object value;
125                            String propertyName = parameterMapping.getProperty();
126                            if (boundSql.hasAdditionalParameter(propertyName)) {
127                                value = boundSql.getAdditionalParameter(propertyName);
128                            } else if (typeHandlerRegistry.hasTypeHandler(parameter.getClass())) {
129                                value = parameter;
130                            } else {
131                                MetaObject metaObject = configuration.newMetaObject(parameter);
132                                value = metaObject.getValue(propertyName);
133                            }
134                            auditMessage.addParams(value);
135                        }
136                    }
137                }
138            }
139            messageCollector.collect(auditMessage);
140        }
141    }
142
143
144    @FunctionalInterface
145    public interface AuditRunnable<T> {
146        T execute() throws SQLException;
147    }
148
149}