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