001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (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 io.jboot.components.event;
017
018import com.jfinal.log.Log;
019import io.jboot.Jboot;
020import io.jboot.core.weight.WeightUtil;
021import io.jboot.utils.*;
022import io.jboot.components.event.annotation.EventConfig;
023
024import java.util.*;
025import java.util.concurrent.*;
026
027
028public class JbootEventManager {
029
030    private static final Log LOG = Log.getLog(JbootEventManager.class);
031    private static JbootEventManager manager = new JbootEventManager();
032
033    private final Map<String, List<JbootEventListener>> asyncListenerMap;
034    private final Map<String, List<JbootEventListener>> listenerMap;
035
036
037    private ExecutorService threadPool;
038
039
040    private JbootEventManager() {
041        asyncListenerMap = new ConcurrentHashMap<>();
042        listenerMap = new ConcurrentHashMap<>();
043        threadPool = NamedThreadPools.newFixedThreadPool("jboot-event");
044
045        initListeners();
046    }
047
048    public static JbootEventManager me() {
049        return manager;
050    }
051
052    private void initListeners() {
053        List<Class<JbootEventListener>> classes = ClassScanner.scanSubClass(JbootEventListener.class, true);
054        if (ArrayUtil.isNullOrEmpty(classes)) {
055            return;
056        }
057        for (Class<JbootEventListener> clazz : classes) {
058            registerListener(clazz);
059        }
060    }
061
062    public void unRegisterListener(Class<? extends JbootEventListener> listenerClass) {
063
064        deleteListner(listenerMap, listenerClass);
065        deleteListner(asyncListenerMap, listenerClass);
066
067        if (Jboot.isDevMode()) {
068            LOG.debug(String.format("listener[%s]-->>unRegisterListener.", listenerClass));
069        }
070
071    }
072
073    private void deleteListner(Map<String, List<JbootEventListener>> map, Class<? extends JbootEventListener> listenerClass) {
074        for (Map.Entry<String, List<JbootEventListener>> entry : map.entrySet()) {
075            JbootEventListener deleteListener = null;
076            for (JbootEventListener listener : entry.getValue()) {
077                if (listener.getClass() == listenerClass) {
078                    deleteListener = listener;
079                }
080            }
081            if (deleteListener != null) {
082                entry.getValue().remove(deleteListener);
083            }
084        }
085    }
086
087    public void registerListener(Class<? extends JbootEventListener> listenerClass) {
088
089        if (listenerClass == null) {
090            return;
091        }
092
093        EventConfig listenerAnnotation = listenerClass.getAnnotation(EventConfig.class);
094        if (listenerAnnotation == null) {
095            //当未配置 EventConfig 的时候,可以手动注册
096            return;
097        }
098
099        String[] actions = AnnotationUtil.get(listenerAnnotation.action());
100        if (actions == null) {
101            LOG.warn("listenerClass[" + listenerAnnotation + "] register fail, because action is null or blank.");
102            return;
103        }
104
105        if (listenerHasRegisterBefore(listenerClass)) {
106            return;
107        }
108
109        JbootEventListener listener = ClassUtil.newInstance(listenerClass);
110        if (listener == null) {
111            return;
112        }
113
114        registerListener(listener, listenerAnnotation.async(), actions);
115
116    }
117
118
119    public void unRegisterListener(JbootEventListener eventListener) {
120        unRegisterListener(eventListener.getClass());
121    }
122
123    /**
124     * 手从初始化 EventListener ,手动注册
125     *
126     * @param eventListener
127     * @param async
128     * @param actions
129     */
130    public void registerListener(JbootEventListener eventListener, boolean async, String... actions) {
131
132        for (String action : actions) {
133            List<JbootEventListener> list = async ? asyncListenerMap.get(action) : listenerMap.get(action);
134
135            if (null == list) {
136                list = new ArrayList<>();
137            }
138
139            if (list.contains(eventListener)) {
140                continue;
141            }
142
143            list.add(eventListener);
144
145            WeightUtil.sort(list);
146
147            if (async) {
148                asyncListenerMap.put(action, list);
149            } else {
150                listenerMap.put(action, list);
151            }
152        }
153
154        if (Jboot.isDevMode()) {
155            LOG.debug(String.format("listener[%s]-->>registered.", eventListener));
156        }
157    }
158
159
160    private boolean listenerHasRegisterBefore(Class<? extends JbootEventListener> listenerClass) {
161        return findFromMap(listenerClass, listenerMap)
162                || findFromMap(listenerClass, asyncListenerMap);
163    }
164
165    private boolean findFromMap(Class<? extends JbootEventListener> listenerClass, Map<String, List<JbootEventListener>> map) {
166        for (Map.Entry<String, List<JbootEventListener>> entry : map.entrySet()) {
167            List<JbootEventListener> listeners = entry.getValue();
168            if (listeners == null || listeners.isEmpty()) {
169                continue;
170            }
171            for (JbootEventListener ml : listeners) {
172                if (listenerClass == ml.getClass()) {
173                    return true;
174                }
175            }
176        }
177        return false;
178    }
179
180    public void publish(final JbootEvent event) {
181        String action = event.getAction();
182
183        List<JbootEventListener> syncListeners = listenerMap.get(action);
184        if (syncListeners != null && !syncListeners.isEmpty()) {
185            invokeListeners(event, syncListeners);
186        }
187
188        List<JbootEventListener> listeners = asyncListenerMap.get(action);
189        if (listeners != null && !listeners.isEmpty()) {
190            invokeListenersAsync(event, listeners);
191        }
192
193    }
194
195    private void invokeListeners(final JbootEvent event, List<JbootEventListener> syncListeners) {
196        for (final JbootEventListener listener : syncListeners) {
197            try {
198                listener.onEvent(event);
199            } catch (Throwable e) {
200                LOG.error(String.format("listener[%s] onEvent is error! ", listener.getClass()), e);
201            }
202        }
203    }
204
205    private void invokeListenersAsync(final JbootEvent event, List<JbootEventListener> listeners) {
206        for (final JbootEventListener listener : listeners) {
207            threadPool.execute(() -> {
208                try {
209                    listener.onEvent(event);
210                } catch (Throwable e) {
211                    LOG.error(String.format("listener[%s] onEvent is error! ", listener.getClass()), e);
212                }
213            });
214        }
215    }
216
217    public ExecutorService getThreadPool() {
218        return threadPool;
219    }
220
221    public void setThreadPool(ExecutorService threadPool) {
222        this.threadPool = threadPool;
223    }
224}