001: /*
002: * EventManager.java
003: *
004: * Version: $Revision: 2074 $
005: *
006: * Date: $Date: 2007-07-19 14:40:11 -0500 (Thu, 19 Jul 2007) $
007: *
008: * Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts
009: * Institute of Technology. All rights reserved.
010: *
011: * Redistribution and use in source and binary forms, with or without
012: * modification, are permitted provided that the following conditions are
013: * met:
014: *
015: * - Redistributions of source code must retain the above copyright
016: * notice, this list of conditions and the following disclaimer.
017: *
018: * - Redistributions in binary form must reproduce the above copyright
019: * notice, this list of conditions and the following disclaimer in the
020: * documentation and/or other materials provided with the distribution.
021: *
022: * - Neither the name of the Hewlett-Packard Company nor the name of the
023: * Massachusetts Institute of Technology nor the names of their
024: * contributors may be used to endorse or promote products derived from
025: * this software without specific prior written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
030: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
032: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
033: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
034: * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
035: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
036: * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
037: * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
038: * DAMAGE.
039: */
040: package org.dspace.event;
041:
042: import java.lang.reflect.Constructor;
043: import java.lang.reflect.InvocationTargetException;
044: import java.util.Enumeration;
045: import java.util.HashMap;
046: import java.util.Iterator;
047: import java.util.Map;
048:
049: import org.apache.commons.pool.KeyedObjectPool;
050: import org.apache.commons.pool.KeyedPoolableObjectFactory;
051: import org.apache.commons.pool.PoolUtils;
052: import org.apache.commons.pool.impl.GenericKeyedObjectPool;
053: import org.apache.log4j.Logger;
054: import org.dspace.core.ConfigurationManager;
055: import org.dspace.core.Context;
056:
057: /**
058: * Class for managing the content event environment. The EventManager mainly
059: * acts as a factory for Dispatchers, which are used by the Context to send
060: * events to consumers. It also contains generally useful utility methods.
061: *
062: * Version: $Revision: 2074 $
063: */
064: public class EventManager {
065: /** log4j category */
066: private static Logger log = Logger.getLogger(EventManager.class);
067:
068: // The name of the default dispatcher assigned to every new context unless
069: // overridden
070: public static final String DEFAULT_DISPATCHER = "default";
071:
072: private static DispatcherPoolFactory dispatcherFactory = null;
073:
074: private static GenericKeyedObjectPool.Config poolConfig = null;
075:
076: // Keyed FIFO Pool of event dispatchers
077: private static KeyedObjectPool dispatcherPool = null;
078:
079: private static HashMap<String, Integer> consumerIndicies = null;
080:
081: private static final String CONSUMER_PFX = "event.consumer.";
082:
083: public EventManager() {
084: initPool();
085: log.info("Event Dispatcher Pool Initialized");
086: }
087:
088: private static void initPool() {
089:
090: if (dispatcherPool == null) {
091:
092: // TODO EVENT Some of these pool configuration
093: // parameters can live in dspace.cfg or a
094: // separate configuration file
095:
096: // TODO EVENT Eviction parameters should be set
097:
098: poolConfig = new GenericKeyedObjectPool.Config();
099: poolConfig.maxActive = 100;
100: poolConfig.maxIdle = 5;
101: poolConfig.maxTotal = 100;
102:
103: try {
104: dispatcherFactory = new DispatcherPoolFactory();
105: dispatcherPool = PoolUtils
106: .synchronizedPool(new GenericKeyedObjectPool(
107: dispatcherFactory, poolConfig));
108:
109: enumerateConsumers();
110:
111: } catch (Exception e) {
112: e.printStackTrace();
113: }
114:
115: }
116: }
117:
118: /**
119: * Get dispatcher for configuration named by "name". Returns cached instance
120: * if one exists.
121: */
122: public static Dispatcher getDispatcher(String name) {
123: if (dispatcherPool == null) {
124: initPool();
125: }
126:
127: if (name == null)
128: name = DEFAULT_DISPATCHER;
129:
130: try {
131: return (Dispatcher) dispatcherPool.borrowObject(name);
132: } catch (Exception e) {
133: throw new RuntimeException(
134: "Unable to aquire dispatcher named " + name, e);
135: }
136:
137: }
138:
139: public static void returnDispatcher(String key, Dispatcher disp) {
140: try {
141: dispatcherPool.returnObject(key, disp);
142: } catch (Exception e) {
143: log.error(e.getMessage(), e);
144: }
145: }
146:
147: protected static int getConsumerIndex(String consumerClass) {
148: Integer index = (Integer) consumerIndicies.get(consumerClass);
149: return index != null ? index.intValue() : -1;
150:
151: }
152:
153: private static void enumerateConsumers() {
154: Enumeration propertyNames = ConfigurationManager
155: .propertyNames();
156: int bitSetIndex = 0;
157:
158: if (consumerIndicies == null) {
159: consumerIndicies = new HashMap<String, Integer>();
160: }
161:
162: while (propertyNames.hasMoreElements()) {
163: String ckey = ((String) propertyNames.nextElement()).trim();
164:
165: if (ckey.startsWith(CONSUMER_PFX)
166: && ckey.endsWith(".class")) {
167: String consumerName = ckey.substring(CONSUMER_PFX
168: .length(), ckey.length() - 6);
169:
170: consumerIndicies.put(consumerName,
171: (Integer) bitSetIndex);
172: bitSetIndex++;
173: }
174: }
175: }
176:
177: static class DispatcherPoolFactory implements
178: KeyedPoolableObjectFactory {
179:
180: // Prefix of keys in DSpace Configuration
181: private static final String PROP_PFX = "event.dispatcher.";
182:
183: // Cache of event dispatchers, keyed by name, for re-use.
184: private static Map<String, String> dispatchers = new HashMap<String, String>();
185:
186: public DispatcherPoolFactory() {
187: parseEventConfig();
188: log.info("");
189: }
190:
191: public Object makeObject(Object dispatcherName)
192: throws Exception {
193:
194: Dispatcher dispatcher = null;
195: String dispClass = dispatchers.get(dispatcherName);
196:
197: if (dispClass != null) {
198: try {
199: // all this to call a constructor with an argument
200: final Class argTypes[] = { String.class };
201: Constructor dc = Class.forName(dispClass)
202: .getConstructor(argTypes);
203: Object args[] = new Object[1];
204: args[0] = dispatcherName;
205: dispatcher = (Dispatcher) dc.newInstance(args);
206:
207: // OK, now get its list of consumers/filters
208: String consumerKey = PROP_PFX + dispatcherName
209: + ".consumers";
210: String consumerList = ConfigurationManager
211: .getProperty(consumerKey);
212: if (consumerList == null) {
213: throw new RuntimeException(
214: "No Configuration entry found for consumer list of event Dispatcher: \""
215: + consumerKey + "\"");
216: }
217:
218: // Consumer list format:
219: // <consumer-name>:<mode>, ...
220: String[] consumerStanza = consumerList.trim()
221: .split("\\s*,\\s*");
222:
223: // I think this should be a fatal error.. --lcs
224: if (consumerStanza.length < 1) {
225: throw new RuntimeException(
226: "Cannot initialize Dispatcher, malformed Configuration value for "
227: + consumerKey);
228: }
229:
230: ConsumerProfile consumerProfile = null;
231:
232: // parts: 0 is name, part 1 is mode.
233: for (int i = 0; i < consumerStanza.length; i++) {
234: consumerProfile = ConsumerProfile
235: .makeConsumerProfile(consumerStanza[i]);
236: consumerProfile.getConsumer().initialize();
237:
238: dispatcher.addConsumerProfile(consumerProfile);
239: }
240: } catch (NoSuchMethodException e) {
241: throw new RuntimeException(
242: "Constructor not found for event dispatcher="
243: + dispatcherName, e);
244: } catch (InvocationTargetException e) {
245: throw new RuntimeException(
246: "Error creating event dispatcher="
247: + dispatcherName, e);
248: } catch (ClassNotFoundException e) {
249: throw new RuntimeException(
250: "Dispatcher/Consumer class not found for event dispatcher="
251: + dispatcherName, e);
252: } catch (InstantiationException e) {
253: throw new RuntimeException(
254: "Dispatcher/Consumer instantiation failure for event dispatcher="
255: + dispatcherName, e);
256: } catch (IllegalAccessException e) {
257: throw new RuntimeException(
258: "Dispatcher/Consumer access failure for event dispatcher="
259: + dispatcherName, e);
260: }
261: } else {
262: throw new RuntimeException(
263: "Requested Dispatcher Does Not Exist In DSpace Configuration!");
264: }
265:
266: return dispatcher;
267:
268: }
269:
270: public void activateObject(Object arg0, Object arg1)
271: throws Exception {
272: // No-op
273: return;
274:
275: }
276:
277: public void destroyObject(Object key, Object dispatcher)
278: throws Exception {
279: Context ctx = new Context();
280:
281: for (Iterator ci = ((Dispatcher) dispatcher).getConsumers()
282: .iterator(); ci.hasNext();) {
283: ConsumerProfile cp = (ConsumerProfile) ci.next();
284: if (cp != null)
285: cp.getConsumer().finish(ctx);
286: }
287: return;
288:
289: }
290:
291: public void passivateObject(Object arg0, Object arg1)
292: throws Exception {
293: // No-op
294: return;
295:
296: }
297:
298: public boolean validateObject(Object arg0, Object arg1) {
299: // No-op
300: return false;
301: }
302:
303: /**
304: * Looks through the configuration for dispatcher configurations and
305: * loads one of each into a HashMap. This Map will be used to clone new
306: * objects when the pool needs them.
307: *
308: * Looks for configuration properties like:
309: *
310: * <pre>
311: * # class of dispatcher "default"
312: * event.dispatcher.default = org.dspace.event.BasicDispatcher
313: * # list of consumers followed by filters for each, format is
314: * # <consumerClass>:<filter>[:<anotherFilter>..] , ...
315: * # and each filter is expressed as:
316: * # <objectType>[|<objectType> ...] + <eventType>[|<eventType> ..]
317: * org.dspace.event.TestConsumer:all+all, \
318: * org.dspace.eperson.SubscribeConsumer:Item+CREATE|DELETE:Collection+ADD, ...
319: * </pre>
320: *
321: */
322: private void parseEventConfig() {
323: Enumeration propertyNames = ConfigurationManager
324: .propertyNames();
325: while (propertyNames.hasMoreElements()) {
326: String ckey = ((String) propertyNames.nextElement())
327: .trim();
328:
329: if (ckey.startsWith(PROP_PFX)
330: && ckey.endsWith(".class")) {
331: String name = ckey.substring(PROP_PFX.length(),
332: ckey.length() - 6);
333: String dispatcherClass = ConfigurationManager
334: .getProperty(ckey);
335:
336: // Can we grab all of the consumers configured for this
337: // dispatcher
338: // and store them also? Then there is no
339: // ConfigurationManager call
340: // upon other makeObject(key) requests resulting in a faster
341: // pool
342: // get.
343:
344: dispatchers.put(name, dispatcherClass);
345:
346: }
347: }
348: }
349: }
350: }
|