001    /*
002     * 
003     * $Revision: 14998 $ $Date: 2009-03-24 14:08:58 +0100 (Tue, 24 Mar 2009) $
004     *
005     * This file is part of ***  M y C o R e  ***
006     * See http://www.mycore.de/ for details.
007     *
008     * This program is free software; you can use it, redistribute it
009     * and / or modify it under the terms of the GNU General Public License
010     * (GPL) as published by the Free Software Foundation; either version 2
011     * of the License or (at your option) any later version.
012     *
013     * This program is distributed in the hope that it will be useful, but
014     * WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program, in a file called gpl.txt or license.txt.
020     * If not, write to the Free Software Foundation Inc.,
021     * 59 Temple Place - Suite 330, Boston, MA  02111-1307 USA
022     */
023    
024    package org.mycore.common.events;
025    
026    import java.util.ArrayList;
027    import java.util.Collections;
028    import java.util.Hashtable;
029    import java.util.List;
030    import java.util.Properties;
031    import java.util.StringTokenizer;
032    
033    import org.apache.log4j.Logger;
034    import org.mycore.common.MCRConfiguration;
035    import org.mycore.common.MCRConfigurationException;
036    import org.mycore.common.MCRException;
037    import org.mycore.services.fieldquery.MCRSearcherFactory;
038    
039    /**
040     * Acts as a multiplexer to forward events that are created to all registered
041     * event handlers, in the order that is configured in mycore properties. For
042     * information how to configure, see MCREventHandler javadocs.
043     * 
044     * @see MCREventHandler
045     * @see MCREventHandlerBase
046     * 
047     * @author Frank Lützenkirchen
048     */
049    public class MCREventManager {
050        private static Logger logger = Logger.getLogger(MCREventManager.class);
051    
052        private static MCREventManager instance;
053    
054        /**
055         * The singleton manager instance
056         * 
057         * @return the single event manager
058         */
059        public static synchronized MCREventManager instance() {
060            if (instance == null) {
061                instance = new MCREventManager();
062            }
063    
064            return instance;
065        }
066    
067        /** Table of all configured event handlers * */
068        private Hashtable<String, List<MCREventHandler>> handlers;
069    
070        private MCREventManager() {
071            handlers = new Hashtable<String, List<MCREventHandler>>();
072    
073            MCRConfiguration config = MCRConfiguration.instance();
074    
075            String prefix = "MCR.EventHandler.";
076    
077            Properties props = config.getProperties(prefix);
078    
079            if (props == null) {
080                return;
081            }
082    
083            List<String> names = new ArrayList<String>(props.size());
084            for (Object name : props.keySet())
085                names.add(name.toString());
086            Collections.sort(names);
087    
088            List<MCREventHandler> instances = null;
089    
090            for (int i = 0; i < names.size(); i++) {
091                String name = (String) (names.get(i));
092    
093                StringTokenizer st = new StringTokenizer(name, ".");
094                st.nextToken();
095                st.nextToken();
096    
097                String type = st.nextToken();
098                int nr = Integer.parseInt(st.nextToken());
099                String mode = st.nextToken(); // "Class" or "Indexer"
100    
101                if (nr == 1) {
102                    instances = new ArrayList<MCREventHandler>();
103                    handlers.put(type, instances);
104                }
105    
106                logger.debug("EventManager instantiating handler " + config.getString(name) + " for type " + type);
107    
108                Object handler = null;
109    
110                if ("Class".equals(mode)) {
111                    if (config.getString(name).length() == 0) {
112                        //if property from mycore.properties is overwritten with empty value
113                        //by mycore.properties.private
114                        continue;
115                    }
116                    handler = config.getSingleInstanceOf(name);
117                } else { // "Indexer"
118                    handler = MCRSearcherFactory.getSearcher(config.getString(name));
119                }
120    
121                if (!(handler instanceof MCREventHandler)) {
122                    String msg = "Error: Class does not implement MCREventHandler: " + name;
123                    throw new MCRConfigurationException(msg);
124                }
125    
126                instances.add((MCREventHandler) handler);
127            }
128        }
129    
130        /** Call event handlers in forward direction (create, update) */
131        public final static boolean FORWARD = true;
132    
133        /** Call event handlers in backward direction (delete) */
134        public final static boolean BACKWARD = false;
135    
136        /**
137         * This method is called by the component that created the event and acts as
138         * a multiplexer that invokes all registered event handlers doHandleEvent
139         * methods. If something goes wrong and an exception is caught, the
140         * undoHandleEvent methods of all event handlers that are at a position
141         * BEFORE the failed one, will be called in reversed order. The parameter
142         * direction controls the order in which the event handlers are called.
143         * 
144         * @see MCREventHandler#doHandleEvent
145         * @see MCREventHandlerBase
146         * 
147         * @param evt
148         *            the event that happened
149         * @param direction
150         *            the order in which the event handlers are called
151         */
152        public void handleEvent(MCREvent evt, boolean direction) {
153            List<MCREventHandler> list = (handlers.get(evt.getObjectType()));
154            if (list == null)
155                return;
156    
157            int first = (direction ? 0 : list.size() - 1);
158            int last = (direction ? list.size() - 1 : 0);
159            int step = (direction ? 1 : -1);
160            int undoPos = first;
161    
162            for (int i = first; i != last + step; i += step) {
163                MCREventHandler eh = list.get(i);
164                logger.debug("EventManager " + evt.getObjectType() + " " + evt.getEventType() + " calling handler " + eh.getClass().getName());
165    
166                try {
167                    eh.doHandleEvent(evt);
168                } catch (Exception ex) {
169                    logger.info("Exception caught while calling event handler", ex);
170                    logger.info("Trying rollback by calling undo method of event handlers");
171    
172                    undoPos = i;
173    
174                    break;
175                }
176            }
177    
178            // Rollback by calling undo of successfull handlers
179            for (int i = undoPos - step; i != first - step; i -= step) {
180                MCREventHandler eh = (MCREventHandler) (list.get(i));
181                logger.debug("EventManager " + evt.getObjectType() + " " + evt.getEventType() + " calling undo of handler "
182                        + eh.getClass().getName());
183    
184                try {
185                    eh.undoHandleEvent(evt);
186                } catch (Exception ex) {
187                    logger.info("Exception caught while calling undo of event handler", ex);
188                }
189            }
190        }
191    
192        /** Same as handleEvent( evt, MCREventManager.FORWARD ) */
193        public void handleEvent(MCREvent evt) throws MCRException {
194            handleEvent(evt, MCREventManager.FORWARD);
195        }
196    }