001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://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: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://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.util.ArrayList;
031import java.util.HashMap;
032import java.util.Iterator;
033import java.util.List;
034import java.util.Map;
035
036import org.apache.commons.logging.Log;
037
038/**
039 * Manager that controls the OpenCms event system.
040 *
041 * There is only one instance of this event manager class used by the OpenCms runtime.
042 * This instance can be obtained by calling {@link OpenCms#getEventManager()}.<p>
043 *
044 * Events can be used in OpenCms to notify custom event listeners that certain system events have happened.
045 * Event listeners have to implement the interface {@link org.opencms.main.I_CmsEventListener}.<p>
046 *
047 * @since 7.0.0
048 *
049 * @see org.opencms.main.CmsEvent
050 * @see org.opencms.main.I_CmsEventListener
051 */
052public class CmsEventManager {
053
054    /** Required as template for event list generation. */
055    protected static final I_CmsEventListener[] EVENT_LIST = new I_CmsEventListener[0];
056
057    /** The static log object for this class. */
058    private static final Log LOG = CmsLog.getLog(CmsEventManager.class);
059
060    /** Stores the active event listeners. */
061    private Map<Integer, List<I_CmsEventListener>> m_eventListeners;
062
063    /**
064     * Create a new instance of an OpenCms event manager.<p>
065     */
066    public CmsEventManager() {
067
068        m_eventListeners = new HashMap<Integer, List<I_CmsEventListener>>();
069    }
070
071    /**
072     * Add an OpenCms event listener that listens to all events.<p>
073     *
074     * @param listener the listener to add
075     */
076    public void addCmsEventListener(I_CmsEventListener listener) {
077
078        addCmsEventListener(listener, null);
079    }
080
081    /**
082     * Add an OpenCms event listener.<p>
083     *
084     * @param listener the listener to add
085     * @param eventTypes the events to listen for
086     */
087    public void addCmsEventListener(I_CmsEventListener listener, int[] eventTypes) {
088
089        synchronized (m_eventListeners) {
090            if (eventTypes == null) {
091                // no event types given - register the listener for all event types
092                eventTypes = new int[] {I_CmsEventListener.LISTENERS_FOR_ALL_EVENTS.intValue()};
093            }
094            for (int i = 0; i < eventTypes.length; i++) {
095                // register the listener for all configured event types
096                Integer eventType = new Integer(eventTypes[i]);
097                List<I_CmsEventListener> listeners = m_eventListeners.get(eventType);
098                if (listeners == null) {
099                    listeners = new ArrayList<I_CmsEventListener>();
100                    m_eventListeners.put(eventType, listeners);
101                }
102                if (!listeners.contains(listener)) {
103                    // add listerner only if it is not already registered
104                    listeners.add(listener);
105                }
106            }
107        }
108    }
109
110    /**
111     * Notify all event listeners that a particular event has occurred.<p>
112     *
113     * @param event the event that is forwarded to all listeners
114     */
115    public void fireEvent(CmsEvent event) {
116
117        fireEventHandler(m_eventListeners.get(event.getTypeInteger()), event);
118        fireEventHandler(m_eventListeners.get(I_CmsEventListener.LISTENERS_FOR_ALL_EVENTS), event);
119    }
120
121    /**
122     * Notify all event listeners that a particular event has occurred without any additional event data.<p>
123     *
124     * @param type event type
125     */
126    public void fireEvent(int type) {
127
128        fireEvent(type, new HashMap<String, Object>());
129    }
130
131    /**
132     * Notify all event listeners that a particular event has occurred.<p>
133     *
134     * @param type event type
135     * @param data event data
136     */
137    public void fireEvent(int type, Map<String, Object> data) {
138
139        fireEvent(new CmsEvent(type, data));
140    }
141
142    /**
143     * Removes a cms event listener.<p>
144     *
145     * @param listener the listener to remove
146     */
147    public void removeCmsEventListener(I_CmsEventListener listener) {
148
149        synchronized (m_eventListeners) {
150            Iterator<Integer> it = m_eventListeners.keySet().iterator();
151            while (it.hasNext()) {
152                List<I_CmsEventListener> listeners = m_eventListeners.get(it.next());
153                listeners.remove(listener);
154            }
155        }
156    }
157
158    /**
159     * Fires the specified event to a list of event listeners.<p>
160     *
161     * @param listeners the listeners to fire
162     * @param event the event to fire
163     */
164    protected void fireEventHandler(List<I_CmsEventListener> listeners, CmsEvent event) {
165
166        if (!LOG.isDebugEnabled()) {
167            // no logging required
168            if ((listeners != null) && (listeners.size() > 0)) {
169                // handle all event listeners that listen to this event type
170                I_CmsEventListener[] list = listeners.toArray(EVENT_LIST);
171                // loop through all registered event listeners
172                for (int i = 0; i < list.length; i++) {
173                    try {
174                        // fire the event
175                        list[i].cmsEvent(event);
176                    } catch (Throwable t) {
177                        LOG.error(
178                            Messages.get().getBundle().key(
179                                Messages.ERR_CALLING_EVENT_LISTENER_FAILED_2,
180                                list[i].getClass().getName(),
181                                event.toString()),
182                            t);
183                    }
184                }
185            }
186        } else {
187            // add lots of event debug output (this should usually be disabled)
188            // repeat event handling code to avoid multiple "is log enabled" checks in normal operation
189            LOG.debug(Messages.get().getBundle().key(Messages.LOG_DEBUG_EVENT_1, event.toString()));
190            if ((listeners != null) && (listeners.size() > 0)) {
191                // handle all event listeners that listen to this event type
192                I_CmsEventListener[] list = listeners.toArray(EVENT_LIST);
193                // log the event data
194                if (event.getData() != null) {
195                    Iterator<String> i = event.getData().keySet().iterator();
196                    while (i.hasNext()) {
197                        String key = i.next();
198                        Object value = event.getData().get(key);
199                        LOG.debug(
200                            Messages.get().getBundle().key(
201                                Messages.LOG_DEBUG_EVENT_VALUE_3,
202                                key,
203                                value,
204                                event.toString()));
205                    }
206                } else {
207                    LOG.debug(Messages.get().getBundle().key(Messages.LOG_DEBUG_NO_EVENT_VALUE_1, event.toString()));
208                }
209                // log all the registered event listeners
210                for (int j = 0; j < list.length; j++) {
211                    LOG.debug(
212                        Messages.get().getBundle().key(
213                            Messages.LOG_DEBUG_EVENT_LISTENERS_3,
214                            list[j],
215                            new Integer(j),
216                            event.toString()));
217                }
218                // loop through all registered event listeners
219                for (int i = 0; i < list.length; i++) {
220                    LOG.debug(
221                        Messages.get().getBundle().key(
222                            Messages.LOG_DEBUG_EVENT_START_LISTENER_3,
223                            list[i],
224                            new Integer(i),
225                            event.toString()));
226                    try {
227                        // fire the event
228                        list[i].cmsEvent(event);
229                    } catch (Throwable t) {
230                        LOG.error(
231                            Messages.get().getBundle().key(
232                                Messages.ERR_CALLING_EVENT_LISTENER_FAILED_2,
233                                list[i].getClass().getName(),
234                                event.toString()),
235                            t);
236                    }
237                    LOG.debug(
238                        Messages.get().getBundle().key(
239                            Messages.LOG_DEBUG_EVENT_END_LISTENER_3,
240                            list[i],
241                            new Integer(i),
242                            event.toString()));
243                }
244            } else {
245                LOG.debug(Messages.get().getBundle().key(Messages.LOG_DEBUG_EVENT_NO_LISTENER_1, event.toString()));
246            }
247            LOG.debug(Messages.get().getBundle().key(Messages.LOG_DEBUG_EVENT_COMPLETE_1, event.toString()));
248        }
249    }
250
251    /**
252     * Returns the map of all configured event listeners.<p>
253     *
254     * @return the map of all configured event listeners
255     */
256    protected Map<Integer, List<I_CmsEventListener>> getEventListeners() {
257
258        return m_eventListeners;
259    }
260
261    /**
262     * Initialize this event manager with all events from the given base event manager.<p>
263     *
264     * @param base the base event manager to initialize this event manager with
265     */
266    protected void initialize(CmsEventManager base) {
267
268        m_eventListeners = new HashMap<Integer, List<I_CmsEventListener>>(base.getEventListeners());
269
270    }
271}