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 }