001: /*
002: * Copyright 2004-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.springframework.webflow.execution.factory;
017:
018: import java.util.Iterator;
019: import java.util.LinkedList;
020: import java.util.List;
021: import java.util.Map;
022: import java.util.Map.Entry;
023:
024: import org.apache.commons.logging.Log;
025: import org.apache.commons.logging.LogFactory;
026: import org.springframework.core.style.StylerUtils;
027: import org.springframework.util.Assert;
028: import org.springframework.util.StringUtils;
029: import org.springframework.webflow.definition.FlowDefinition;
030: import org.springframework.webflow.execution.FlowExecutionListener;
031:
032: /**
033: * A flow execution listener loader that stores listeners in a list-backed data
034: * structure and allows for configuration of which listeners should apply to
035: * which flow definitions. For trivial listener loading, see
036: * {@link StaticFlowExecutionListenerLoader}.
037: *
038: * @see StaticFlowExecutionListenerLoader
039: *
040: * @author Keith Donald
041: */
042: public class ConditionalFlowExecutionListenerLoader implements
043: FlowExecutionListenerLoader {
044:
045: /**
046: * Logger, usable by subclasses.
047: */
048: protected final Log logger = LogFactory.getLog(getClass());
049:
050: /**
051: * The list of flow execution listeners containing
052: * {@link ConditionalFlowExecutionListenerHolder} objects. The list
053: * determines the conditions in which a single flow execution listener
054: * applies.
055: */
056: private List listeners = new LinkedList();
057:
058: /**
059: * Add a listener that will listen to executions for all flows.
060: * @param listener the listener to add
061: */
062: public void addListener(FlowExecutionListener listener) {
063: addListener(listener, null);
064: }
065:
066: /**
067: * Adds a collection of listeners that share a matching criteria.
068: * @param listeners the listeners
069: * @param criteria the criteria where these listeners apply
070: */
071: public void addListeners(FlowExecutionListener[] listeners,
072: FlowExecutionListenerCriteria criteria) {
073: for (int i = 0; i < listeners.length; i++) {
074: addListener(listeners[i], criteria);
075: }
076: }
077:
078: /**
079: * Add a listener that will listen to executions to flows matching the
080: * specified criteria.
081: * @param listener the listener
082: * @param criteria the listener criteria
083: */
084: public void addListener(FlowExecutionListener listener,
085: FlowExecutionListenerCriteria criteria) {
086: if (listener == null) {
087: return;
088: }
089: if (logger.isDebugEnabled()) {
090: logger.debug("Adding flow execution listener " + listener
091: + " with criteria " + criteria);
092: }
093: ConditionalFlowExecutionListenerHolder conditional = getHolder(listener);
094: if (conditional == null) {
095: conditional = new ConditionalFlowExecutionListenerHolder(
096: listener);
097: listeners.add(conditional);
098: }
099: if (criteria == null) {
100: criteria = new FlowExecutionListenerCriteriaFactory()
101: .allFlows();
102: }
103: conditional.add(criteria);
104: }
105:
106: /**
107: * Set the list of flow execution listeners with corresponding criteria.
108: * Allows for bean style configuration. The given map should have
109: * {@link FlowExecutionListener} objects as keys and Strings ("*", "flowId",
110: * "flowId1,flowId2") or {@link FlowExecutionListenerCriteria}
111: * objects as values. This will clear any listeners registered with
112: * this object using the <tt>addListener</tt> methods.
113: * @param listenersWithCriteria the map of listeners and their corresponding criteria
114: */
115: public void setListeners(Map listenersWithCriteria) {
116: removeAllListeners();
117: for (Iterator it = listenersWithCriteria.entrySet().iterator(); it
118: .hasNext();) {
119: Entry entry = (Entry) it.next();
120: Assert
121: .isInstanceOf(
122: FlowExecutionListener.class,
123: entry.getKey(),
124: "The key in the listenersWithCriteria map needs to be a FlowExecutionListener object");
125: FlowExecutionListener listener = (FlowExecutionListener) entry
126: .getKey();
127: FlowExecutionListenerCriteria criteria = null;
128: if (entry.getValue() instanceof String) {
129: criteria = getCriteria((String) entry.getValue());
130: } else if (entry.getValue() instanceof FlowExecutionListenerCriteria) {
131: criteria = (FlowExecutionListenerCriteria) entry
132: .getValue();
133: } else if (entry.getValue() != null) {
134: throw new IllegalArgumentException(
135: "The value in the listenersWithCriteria map needs to be a "
136: + "String or a FlowExecutionListenerCriteria object");
137: }
138: addListener(listener, criteria);
139: }
140: }
141:
142: /**
143: * Is the given listener contained by this Flow execution manager?
144: * @param listener the listener
145: * @return true if yes, false otherwise
146: */
147: public boolean containsListener(FlowExecutionListener listener) {
148: Iterator it = listeners.iterator();
149: while (it.hasNext()) {
150: ConditionalFlowExecutionListenerHolder h = (ConditionalFlowExecutionListenerHolder) it
151: .next();
152: if (h.getListener().equals(listener)) {
153: return true;
154: }
155: }
156: return false;
157: }
158:
159: /**
160: * Remove the flow execution listener from the listener list.
161: * @param listener the listener
162: */
163: public void removeListener(FlowExecutionListener listener) {
164: Iterator it = listeners.iterator();
165: while (it.hasNext()) {
166: ConditionalFlowExecutionListenerHolder h = (ConditionalFlowExecutionListenerHolder) it
167: .next();
168: if (h.getListener().equals(listener)) {
169: it.remove();
170: }
171: }
172: }
173:
174: /**
175: * Remove all listeners loadable by this loader.
176: */
177: public void removeAllListeners() {
178: listeners.clear();
179: }
180:
181: /**
182: * Remove the criteria for the specified listener.
183: * @param listener the listener
184: * @param criteria the criteria
185: */
186: public void removeListenerCriteria(FlowExecutionListener listener,
187: FlowExecutionListenerCriteria criteria) {
188: if (containsListener(listener)) {
189: ConditionalFlowExecutionListenerHolder listenerHolder = getHolder(listener);
190: listenerHolder.remove(criteria);
191: if (listenerHolder.isCriteriaSetEmpty()) {
192: removeListener(listener);
193: }
194: }
195: }
196:
197: /**
198: * Returns the array of flow execution listeners for specified flow.
199: * @param flowDefinition the flow definition associated with the execution
200: * to be listened to
201: * @return the flow execution listeners that apply
202: */
203: public FlowExecutionListener[] getListeners(
204: FlowDefinition flowDefinition) {
205: Assert.notNull(flowDefinition,
206: "The Flow to load listeners for cannot be null");
207: List listenersToAttach = new LinkedList();
208: for (Iterator it = listeners.iterator(); it.hasNext();) {
209: ConditionalFlowExecutionListenerHolder listenerHolder = (ConditionalFlowExecutionListenerHolder) it
210: .next();
211: if (listenerHolder.listenerAppliesTo(flowDefinition)) {
212: listenersToAttach.add(listenerHolder.getListener());
213: }
214: }
215: if (logger.isDebugEnabled()) {
216: logger
217: .debug("Loaded ["
218: + listenersToAttach.size()
219: + "] of possible "
220: + listeners.size()
221: + " listeners for this execution request for flow '"
222: + flowDefinition.getId()
223: + "', the listeners to attach are "
224: + StylerUtils.style(listenersToAttach));
225: }
226: return (FlowExecutionListener[]) listenersToAttach
227: .toArray(new FlowExecutionListener[listenersToAttach
228: .size()]);
229: }
230:
231: // internal helpers
232:
233: /**
234: * Lookup the listener criteria holder for the listener provided.
235: * @param listener the listener
236: * @return the holder, or null if not found
237: */
238: private ConditionalFlowExecutionListenerHolder getHolder(
239: FlowExecutionListener listener) {
240: Iterator it = listeners.iterator();
241: while (it.hasNext()) {
242: ConditionalFlowExecutionListenerHolder next = (ConditionalFlowExecutionListenerHolder) it
243: .next();
244: if (next.getListener().equals(listener)) {
245: return next;
246: }
247: }
248: return null;
249: }
250:
251: /**
252: * Decode given string value into one of the well known criteria types.
253: * @see FlowExecutionListenerCriteriaFactory
254: */
255: protected FlowExecutionListenerCriteria getCriteria(String value) {
256: if ("*".equals(value)) {
257: return new FlowExecutionListenerCriteriaFactory()
258: .allFlows();
259: } else {
260: String[] flowIds = StringUtils
261: .commaDelimitedListToStringArray(value);
262: for (int i = 0; i < flowIds.length; i++) {
263: flowIds[i] = flowIds[i].trim();
264: }
265: return new FlowExecutionListenerCriteriaFactory()
266: .flows(flowIds);
267: }
268: }
269:
270: }
|