001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.main; 029 030import java.lang.reflect.Field; 031import java.lang.reflect.Modifier; 032import java.util.ArrayList; 033import java.util.HashMap; 034import java.util.Iterator; 035import java.util.List; 036import java.util.Map; 037import java.util.concurrent.ConcurrentHashMap; 038 039import org.apache.commons.logging.Log; 040 041/** 042 * Manager that controls the OpenCms event system. 043 * 044 * There is only one instance of this event manager class used by the OpenCms runtime. 045 * This instance can be obtained by calling {@link OpenCms#getEventManager()}.<p> 046 * 047 * Events can be used in OpenCms to notify custom event listeners that certain system events have happened. 048 * Event listeners have to implement the interface {@link org.opencms.main.I_CmsEventListener}.<p> 049 * 050 * @since 7.0.0 051 * 052 * @see org.opencms.main.CmsEvent 053 * @see org.opencms.main.I_CmsEventListener 054 */ 055public class CmsEventManager { 056 057 /** Required as template for event list generation. */ 058 protected static final I_CmsEventListener[] EVENT_LIST = new I_CmsEventListener[0]; 059 060 /** The static log object for this class. */ 061 private static final Log LOG = CmsLog.getLog(CmsEventManager.class); 062 063 /** Maps event type ids to the corresponding field names - for debug purposes. */ 064 private static ConcurrentHashMap<Integer, String> m_eventNames = new ConcurrentHashMap<>(); 065 066 /** Stores the active event listeners. */ 067 private Map<Integer, List<I_CmsEventListener>> m_eventListeners; 068 069 /** 070 * Create a new instance of an OpenCms event manager.<p> 071 */ 072 public CmsEventManager() { 073 074 m_eventListeners = new HashMap<Integer, List<I_CmsEventListener>>(); 075 } 076 077 /** 078 * Finds the field name in I_CmsEventListener for a specific event type. 079 * 080 * <p>For debugging/logging. 081 * 082 * @param eventType the event type id 083 * @return the field name 084 */ 085 public static String getEventName(int eventType) { 086 087 return m_eventNames.computeIfAbsent(Integer.valueOf(eventType), k -> "" + k); 088 } 089 090 /** 091 * Add an OpenCms event listener that listens to all events.<p> 092 * 093 * @param listener the listener to add 094 */ 095 public void addCmsEventListener(I_CmsEventListener listener) { 096 097 addCmsEventListener(listener, null); 098 } 099 100 /** 101 * Add an OpenCms event listener.<p> 102 * 103 * @param listener the listener to add 104 * @param eventTypes the events to listen for 105 */ 106 public void addCmsEventListener(I_CmsEventListener listener, int[] eventTypes) { 107 108 synchronized (m_eventListeners) { 109 if (eventTypes == null) { 110 // no event types given - register the listener for all event types 111 eventTypes = new int[] {I_CmsEventListener.LISTENERS_FOR_ALL_EVENTS.intValue()}; 112 } 113 for (int i = 0; i < eventTypes.length; i++) { 114 // register the listener for all configured event types 115 Integer eventType = Integer.valueOf(eventTypes[i]); 116 List<I_CmsEventListener> listeners = m_eventListeners.get(eventType); 117 if (listeners == null) { 118 listeners = new ArrayList<I_CmsEventListener>(); 119 m_eventListeners.put(eventType, listeners); 120 } 121 if (!listeners.contains(listener)) { 122 // add listerner only if it is not already registered 123 listeners.add(listener); 124 } 125 } 126 } 127 } 128 129 /** 130 * Notify all event listeners that a particular event has occurred.<p> 131 * 132 * @param event the event that is forwarded to all listeners 133 */ 134 public void fireEvent(CmsEvent event) { 135 136 fireEventHandler(m_eventListeners.get(event.getTypeInteger()), event); 137 fireEventHandler(m_eventListeners.get(I_CmsEventListener.LISTENERS_FOR_ALL_EVENTS), event); 138 } 139 140 /** 141 * Notify all event listeners that a particular event has occurred without any additional event data.<p> 142 * 143 * @param type event type 144 */ 145 public void fireEvent(int type) { 146 147 fireEvent(type, new HashMap<String, Object>()); 148 } 149 150 /** 151 * Notify all event listeners that a particular event has occurred.<p> 152 * 153 * @param type event type 154 * @param data event data 155 */ 156 public void fireEvent(int type, Map<String, Object> data) { 157 158 fireEvent(new CmsEvent(type, data)); 159 } 160 161 /** 162 * Removes a cms event listener.<p> 163 * 164 * @param listener the listener to remove 165 */ 166 public void removeCmsEventListener(I_CmsEventListener listener) { 167 168 synchronized (m_eventListeners) { 169 Iterator<Integer> it = m_eventListeners.keySet().iterator(); 170 while (it.hasNext()) { 171 List<I_CmsEventListener> listeners = m_eventListeners.get(it.next()); 172 listeners.remove(listener); 173 } 174 } 175 } 176 177 /** 178 * Fires the specified event to a list of event listeners.<p> 179 * 180 * @param listeners the listeners to fire 181 * @param event the event to fire 182 */ 183 protected void fireEventHandler(List<I_CmsEventListener> listeners, CmsEvent event) { 184 185 if (!LOG.isDebugEnabled()) { 186 // no logging required 187 if ((listeners != null) && (listeners.size() > 0)) { 188 // handle all event listeners that listen to this event type 189 I_CmsEventListener[] list = listeners.toArray(EVENT_LIST); 190 // loop through all registered event listeners 191 for (int i = 0; i < list.length; i++) { 192 try { 193 // fire the event 194 list[i].cmsEvent(event); 195 } catch (Throwable t) { 196 LOG.error( 197 Messages.get().getBundle().key( 198 Messages.ERR_CALLING_EVENT_LISTENER_FAILED_2, 199 list[i].getClass().getName(), 200 event.toString()), 201 t); 202 } 203 } 204 } 205 } else { 206 // add lots of event debug output (this should usually be disabled) 207 // repeat event handling code to avoid multiple "is log enabled" checks in normal operation 208 LOG.debug(Messages.get().getBundle().key(Messages.LOG_DEBUG_EVENT_1, event.toString())); 209 if ((listeners != null) && (listeners.size() > 0)) { 210 // handle all event listeners that listen to this event type 211 I_CmsEventListener[] list = listeners.toArray(EVENT_LIST); 212 // log the event data 213 if (event.getData() != null) { 214 Iterator<String> i = event.getData().keySet().iterator(); 215 while (i.hasNext()) { 216 String key = i.next(); 217 Object value = event.getData().get(key); 218 LOG.debug( 219 Messages.get().getBundle().key( 220 Messages.LOG_DEBUG_EVENT_VALUE_3, 221 key, 222 value, 223 event.toString())); 224 } 225 } else { 226 LOG.debug(Messages.get().getBundle().key(Messages.LOG_DEBUG_NO_EVENT_VALUE_1, event.toString())); 227 } 228 // log all the registered event listeners 229 for (int j = 0; j < list.length; j++) { 230 LOG.debug( 231 Messages.get().getBundle().key( 232 Messages.LOG_DEBUG_EVENT_LISTENERS_3, 233 list[j], 234 Integer.valueOf(j), 235 event.toString())); 236 } 237 // loop through all registered event listeners 238 for (int i = 0; i < list.length; i++) { 239 LOG.debug( 240 Messages.get().getBundle().key( 241 Messages.LOG_DEBUG_EVENT_START_LISTENER_3, 242 list[i], 243 Integer.valueOf(i), 244 event.toString())); 245 try { 246 // fire the event 247 list[i].cmsEvent(event); 248 } catch (Throwable t) { 249 LOG.error( 250 Messages.get().getBundle().key( 251 Messages.ERR_CALLING_EVENT_LISTENER_FAILED_2, 252 list[i].getClass().getName(), 253 event.toString()), 254 t); 255 } 256 LOG.debug( 257 Messages.get().getBundle().key( 258 Messages.LOG_DEBUG_EVENT_END_LISTENER_3, 259 list[i], 260 Integer.valueOf(i), 261 event.toString())); 262 } 263 } else { 264 LOG.debug(Messages.get().getBundle().key(Messages.LOG_DEBUG_EVENT_NO_LISTENER_1, event.toString())); 265 } 266 LOG.debug(Messages.get().getBundle().key(Messages.LOG_DEBUG_EVENT_COMPLETE_1, event.toString())); 267 } 268 } 269 270 /** 271 * Returns the map of all configured event listeners.<p> 272 * 273 * @return the map of all configured event listeners 274 */ 275 protected Map<Integer, List<I_CmsEventListener>> getEventListeners() { 276 277 return m_eventListeners; 278 } 279 280 /** 281 * Initialize this event manager with all events from the given base event manager.<p> 282 * 283 * @param base the base event manager to initialize this event manager with 284 */ 285 protected void initialize(CmsEventManager base) { 286 287 m_eventListeners = new HashMap<Integer, List<I_CmsEventListener>>(base.getEventListeners()); 288 try { 289 for (Field field : I_CmsEventListener.class.getDeclaredFields()) { 290 if (Modifier.isStatic(field.getModifiers())) { 291 if ((field.getType() == int.class) && field.getName().startsWith("EVENT_")) { 292 m_eventNames.put(Integer.valueOf(field.getInt(null)), field.getName()); 293 } 294 } 295 } 296 } catch (Exception e) { 297 LOG.error(e.getLocalizedMessage(), e); 298 } 299 } 300}