001: /*--
002:
003: Copyright (C) 2002-2005 Adrian Price.
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009:
010: 1. Redistributions of source code must retain the above copyright
011: notice, this list of conditions, and the following disclaimer.
012:
013: 2. Redistributions in binary form must reproduce the above copyright
014: notice, this list of conditions, and the disclaimer that follows
015: these conditions in the documentation and/or other materials
016: provided with the distribution.
017:
018: 3. The names "OBE" and "Open Business Engine" must not be used to
019: endorse or promote products derived from this software without prior
020: written permission. For written permission, please contact
021: adrianprice@sourceforge.net.
022:
023: 4. Products derived from this software may not be called "OBE" or
024: "Open Business Engine", nor may "OBE" or "Open Business Engine"
025: appear in their name, without prior written permission from
026: Adrian Price (adrianprice@users.sourceforge.net).
027:
028: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
029: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
030: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
031: DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
032: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
033: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
034: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
035: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
036: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
037: IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
038: POSSIBILITY OF SUCH DAMAGE.
039:
040: For more information on OBE, please see
041: <http://obe.sourceforge.net/>.
042:
043: */
044:
045: package org.obe.spi.service;
046:
047: import org.obe.client.api.repository.EventTypeMetaData;
048: import org.obe.client.api.repository.ObjectAlreadyExistsException;
049: import org.obe.client.api.repository.ObjectNotFoundException;
050: import org.obe.client.api.repository.RepositoryException;
051: import org.obe.spi.WorkflowContext;
052: import org.obe.spi.WorkflowService;
053: import org.obe.spi.evaluator.EvaluatorException;
054: import org.obe.spi.event.ApplicationEvent;
055: import org.obe.spi.event.ApplicationEventListener;
056: import org.obe.spi.runtime.BusinessCalendar;
057: import org.obe.spi.util.AsyncFireApplicationEvent;
058: import org.obe.xpdl.model.condition.ConditionType;
059: import org.obe.xpdl.model.misc.Duration;
060:
061: import java.util.Date;
062: import java.util.Map;
063:
064: /**
065: * Provides access to temporal events and external events originating in third
066: * party applications.
067: * <p/>
068: * The workflow engine subscribes to events by specifying the event type, an
069: * optional set of event keys, an optional predicate, and a set of correlation
070: * keys. The combination of event type, event keys and predicate are used to
071: * discriminate the precise event instance to which the engine is subscribing.
072: * The correlation keys encapsulate the workflow context and typically contain
073: * between one and four strings: processDefinitionId, processInstanceId,
074: * activityInstanceId and transitionId. All elements of the array must be
075: * non-null.
076: * <p/>
077: * This somewhat complex scheme is intended to enhance performance, since in a
078: * production system the alternative of evaluating large numbers of simple
079: * expressions would not perform well. The event type definitions and the
080: * expressions used to compute the key values from the inbound event data are
081: * registered in the event broker.
082: * <p/>
083: * When unsubscribing, all existing subscriptions which match the array of
084: * correlation keys are cancelled, even if it contains fewer elements than the
085: * existing subscription. This enables a process instance, for example, to
086: * cancel all outstanding subscriptions in one call by passing just the
087: * processDefinitionId and processInstanceId.
088: *
089: * @author Adrian Price
090: * @see ApplicationEvent
091: * @see ApplicationEventListener
092: */
093: public interface ApplicationEventBroker extends WorkflowService {
094: String SERVICE_NAME = "ApplicationEventBroker";
095:
096: /**
097: * Describes an event subscription.
098: */
099: interface EventSubscription {
100: /**
101: * Returns the event type for which the caller is subscribing.
102: *
103: * @return Event type.
104: */
105: String getEventType();
106:
107: /**
108: * Returns the correlation keys supplied when the subscription was
109: * created. The correlation keys are used to route the event to the
110: * process definition, process instance, activity instance, transition,
111: * etc., which is waiting for the specified event.
112: *
113: * @return Caller's correlation keys.
114: */
115: String[] getCorrelationKeys();
116:
117: /**
118: * Returns the date at which the subscription commences. The system will
119: * not fulfil the subscription before this time. If <code>null</code>
120: * the subscription will is effective immediately.
121: *
122: * @return The effective date.
123: */
124: Date getEffective();
125:
126: /**
127: * Returns the date at which the subscription expires. The system will
128: * not store or honour the subscription after that time. If
129: * <code>null</code>, the subscription will be canceled as soon as it
130: * has been fulfilled by a matching event.
131: *
132: * @return The expiry date.
133: */
134: Date getExpiry();
135:
136: /**
137: * Returns the number of such events to deliver. The subscription
138: * automatically expires when the last event has been delivered.
139: *
140: * @return The event count.
141: */
142: int getCount();
143:
144: /**
145: * Decrements the remaining event count. When the count reaches 0 the
146: * subscription is automatically deleted. Infinitely recurring
147: * subscriptions always return <code>-1</code>.
148: *
149: * @return The new count (or <code>-1</code>).
150: */
151: int decrementCount();
152:
153: /**
154: * Cancels the event subscription. No further access to the
155: * subscription is permitted after it has been cancelled.
156: */
157: void cancel();
158: }
159:
160: /**
161: * Describes a subscription to an application event.
162: */
163: interface ApplicationEventSubscription extends EventSubscription {
164: /**
165: * Returns the application event keys with which the caller is
166: * subscribing. These keys determine whether an instance of the
167: * specified application event type triggers this subscription.
168: *
169: * @return Event keys.
170: */
171: Object[] getEventKeys();
172:
173: /**
174: * Returns the application event condition with which the caller is
175: * subscribing. This condition determines whether an instance of the
176: * specified application event type triggers this subscription.
177: *
178: * @return Event condition.
179: */
180: String getPredicate();
181:
182: /**
183: * Indicates the type of condition. Values are as per
184: * {@link ConditionType}.
185: *
186: * @return The condition type.
187: */
188: int getConditionType();
189: }
190:
191: /**
192: * Describes a subscription to one or more temporal events.
193: */
194: interface TemporalEventSubscription extends EventSubscription {
195: /**
196: * Returns the interval between events in the series.
197: *
198: * @return The interval specification.
199: */
200: Duration getInterval();
201:
202: /**
203: * Returns the name of the
204: * {@link BusinessCalendar BusinessCalendar} to use
205: * when performing temporal computations. Can be <code>null</code>.
206: *
207: * @return The business calendar name.
208: */
209: String getCalendar();
210:
211: /**
212: * Returns whether events matching the subscription are recoverable.
213: * Recoverable subscriptions are notified of events that were missed
214: * because the server was not running at the time they occurred.
215: *
216: * @return <code>true</code> if recoverable.
217: */
218: boolean isRecoverable();
219: }
220:
221: /**
222: * Registers an application event type.
223: *
224: * @param eventType The application event type definition.
225: * @throws ObjectAlreadyExistsException if the event type is already
226: * registered.
227: * @throws RepositoryException if the application event type could not be
228: * registered for some other reason.
229: */
230: void createEventType(EventTypeMetaData eventType)
231: throws RepositoryException;
232:
233: /**
234: * Unregisters an application event type.
235: *
236: * @param eventType The application event name.
237: * @throws ObjectNotFoundException if the application event name is not
238: * registered.
239: * @throws RepositoryException if the application event type could not be
240: * unregistered for some other reason.
241: */
242: void deleteEventType(String eventType) throws RepositoryException;
243:
244: /**
245: * Updates an application event type.
246: *
247: * @param eventType The application event type definition.
248: * @throws ObjectNotFoundException if the application event name is not
249: * registered.
250: * @throws RepositoryException if the application event type could not be
251: * updated for some other reason.
252: */
253: void updateEventType(EventTypeMetaData eventType)
254: throws RepositoryException;
255:
256: /**
257: * Finds meta-data for all application event types. This method is intended
258: * to support design-time clients and management applications.
259: *
260: * @return The list of all application event types.
261: * @throws RepositoryException if an error occurred.
262: */
263: EventTypeMetaData[] findEventTypeMetaData()
264: throws RepositoryException;
265:
266: /**
267: * Finds meta-data for a specific application event type.
268: *
269: * @param eventId The application event ID.
270: * @return Meta-data for the event type.
271: * @throws RepositoryException
272: */
273: EventTypeMetaData findEventTypeMetaData(String eventId)
274: throws RepositoryException;
275:
276: /**
277: * Adds an application event listener. The event listener will be notified
278: * of all events for which a matching subscription has been registered.
279: *
280: * @param listener The listener.
281: * @see #subscribe
282: * @see #removeApplicationEventListener
283: */
284: void addApplicationEventListener(ApplicationEventListener listener);
285:
286: /**
287: * Removes an application event listener. The event listener will no longer
288: * be notified of events, matching or otherwise.
289: *
290: * @param listener The listener.
291: * @see #addApplicationEventListener
292: */
293: void removeApplicationEventListener(
294: ApplicationEventListener listener);
295:
296: /**
297: * Notifies listeners of an event which matches the specified correlation
298: * keys. <em>This method is present solely to support custom event broker
299: * implementations. It should not be called by client code.</em>
300: *
301: * @param event The matching event.
302: * @param correlationKeys The correlation keys from the matched
303: * subscription.
304: * @see AsyncFireApplicationEvent
305: */
306: void fireApplicationEvent(ApplicationEvent event,
307: String[] correlationKeys);
308:
309: /**
310: * Injects an application event for matching. This overload of the
311: * <code>publish</code> method provides a loosely coupled integration
312: * mechanism that imposes minimal requirements on the source application.
313: * <p/>
314: * Matching subscriptions which do not specify an expiry date are cancelled
315: * when they have been fulfilled.
316: * <p/>
317: * If <code>contentType</code> is not specified, the method infers it from
318: * the event <code>data</code> by calling @link #process(Object).
319: * Similarly, if <code>schema</code> is not specified, the method infers it
320: * from the event <code>data</code>. For a <code>contentType</code> of
321: * <code>text/xml</code>, the <code>schema</code> is of the form
322: * <code>{<namespace>}<tag-name></code>, where
323: * <code><namespace></code> is either the PublicId or SystemId of a
324: * DTD if specified, or the namespace URI of the document element. If no
325: * <namespace> is present, the <code>schema</code> is simply
326: * <code><tag-name></code>. For a <code>contentType</code> of
327: * <code>application/x-java-object</code>, the <code>schema</code> is the
328: * fully qualified Java class name.
329: *
330: * @param source The source object which raised the event.
331: * @param attrs Additional metadata about the event data.
332: * @see #publish(ApplicationEvent)
333: */
334: void publish(Object source, Map attrs) throws RepositoryException,
335: EvaluatorException;
336:
337: /**
338: * Injects an application event for matching. This overload of the
339: * <code>publish</code> method provides a tightly coupled integration
340: * mechanism that imposes certain requirements on the source application.
341: * <p/>
342: * Matching subscriptions which do not specify an expiry date are cancelled
343: * when they have been fulfilled.
344: *
345: * @param event The application event.
346: * @see #publish(Object,Map)
347: */
348: void publish(ApplicationEvent event) throws RepositoryException,
349: EvaluatorException;
350:
351: /**
352: * Whether this event broker implementation supports key-based
353: * subscriptions.
354: *
355: * @return <code>true</code> if it does.
356: */
357: boolean supportsKeyBasedSubscriptions();
358:
359: /**
360: * Subscribes to an application event. The event broker uses the specified
361: * event type, keys and predicate to match a particular event occurrence
362: * to the subscription.
363: * <p/>
364: * The matching events and the correlation keys from the subscription are
365: * delivered to the external event listener that was registered in a prior
366: * call to <code>addApplicationEventListener</code>.
367: *
368: * @param eventType The name of the event type. Must be registered with the
369: * event broker.
370: * @param eventKeys The keys required to identify the particular event
371: * occurrence of interest. The keys act as a coarse grained filter that can
372: * be efficiently compared with the corresponding keys on an inbound event.
373: * Can be <code>null</code>, but beware the potential performance impact if
374: * <code>predicate</code> is not <code>null</code>, because the event broker
375: * would have to evaluate all such conditions for every occurrence of
376: * <code>eventType</code>.
377: * @param predicate An additional qualifying condition required to identify
378: * the particular event occurrence of interest. The predicate is evaluated
379: * against the inbound {@link ApplicationEvent}.
380: * @param effective The date/time at which the subscription becomes
381: * effective. Can be <code>null</code>, in which case the subscription
382: * becomes effective immediately.
383: * @param expiry The date/time at which the subscription expires. Can be
384: * <code>null</code>, in which case the subscription never expires.
385: * @param count The number of such events to deliver. The subscription
386: * automatically expires when the <code>count</code><i>th</i> event has been
387: * delivered or when <code>expiry</code> elapses, whichever occurs sooner.
388: * Constraint: <code>count == -1 || count >= 1</code>.
389: * @param correlationKeys Contextual information that will be passed to the
390: * event listener when the event occurs. Typically contains unique keys
391: * for various workflow entities.
392: * @param ctx Workflow execution context.
393: * @return The event subscription.
394: * @throws RepositoryException if the subscription could not be created.
395: * @see #addApplicationEventListener
396: * @see #unsubscribe
397: */
398: ApplicationEventSubscription subscribe(String eventType,
399: Object[] eventKeys, String predicate, Date effective,
400: Date expiry, int count, String[] correlationKeys,
401: WorkflowContext ctx) throws RepositoryException;
402:
403: /**
404: * Subscribes to a single timed event. The event broker schedules the
405: * delivery of an event of the specified type using the correlation keys
406: * supplied. Such subscriptions are always recoverable.
407: * <p/>
408: * The matching event and the correlation keys from the subscription is
409: * delivered to the external event listener that was registered in a prior
410: * call to <code>addApplicationEventListener</code>.
411: *
412: * @param eventType The name of the event type.
413: * @param effective The date/time at which to deliver the event.
414: * @param correlationKeys Contextual information that will be passed to the
415: * event listener when the event occurs. Typically contains unique keys
416: * for various workflow entities.
417: * @return The event subscription.
418: * @throws RepositoryException if the subscription could not be created.
419: * @see #subscribe(String,Date,Date,int,Duration,String,boolean,String[])
420: * @see #unsubscribe
421: * @see #addApplicationEventListener
422: */
423: TemporalEventSubscription subscribe(String eventType,
424: Date effective, String[] correlationKeys)
425: throws RepositoryException;
426:
427: /**
428: * Subscribes to a series of timed events. The event broker schedules
429: * the delivery of a series of up to <code>count</code> events of the
430: * specified <code>eventType</code>. The subscription expires when that
431: * number of events has been delivered or at the specified
432: * <code>expiry</code> date, whichever is sooner. The delivery time for the
433: * next event in the series is computed against the specified
434: * <code>calendar</code> by adding <code>interval</code> to the previous
435: * <code>effective</code> date.
436: * <p/>
437: * The matching events and the correlation keys from the subscription are
438: * delivered to the external event listener that was registered in a prior
439: * call to <code>addApplicationEventListener</code>.
440: *
441: * @param eventType The name of the event type.
442: * @param effective The date/time at which to deliver the first event.
443: * @param expiry The date/time at which the subscription expires. Can be
444: * <code>null</code>.
445: * @param count The number of such events to deliver. The subscription
446: * automatically expires when the <code>count</code><i>th</i> event has been
447: * delivered or when <code>expiry</code> elapses, whichever occurs sooner.
448: * Constraint: <code>count == -1 || count >= 1</code>.
449: * @param interval The interval between events in the series.
450: * @param calendar The name of the
451: * {@link BusinessCalendar BusinessCalendar} to use when
452: * performing temporal computations. Can be <code>null</code>.
453: * @param recoverable <code>true</code> if the temporal events are
454: * recoverable (i.e., must still be processed even if they occurred while
455: * the server was down).
456: * @param correlationKeys Contextual information that will be passed to the
457: * event listener when the event occurs. Typically contains unique keys
458: * for various workflow entities.
459: * @return The event subscription.
460: * @throws RepositoryException if the subscription could not be created.
461: * @see #subscribe(String, Date, String[])
462: * @see #unsubscribe
463: * @see #addApplicationEventListener
464: */
465: TemporalEventSubscription subscribe(String eventType,
466: Date effective, Date expiry, int count, Duration interval,
467: String calendar, boolean recoverable,
468: String[] correlationKeys) throws RepositoryException;
469:
470: /**
471: * Cancels matching subscriptions. The subscriptions must have been
472: * established by a prior call to {@link #subscribe}.
473: *
474: * @param correlationKeys Keys identifying the subscriptions to cancel.
475: * @param exact <code>true</code> to cancel only those subscriptions whose
476: * correlationKeys match exactly in number and value. <code>false</code>
477: * permits all subscriptions within a particular scope to be cancelled. For
478: * example, <code>{x,y}</code> would match <code>{x,y}, {x,y,z1} ... {x,y,zN}
479: * </code> etc. inexactly, but would only match <code>{x,y}</code> exactly.
480: * @throws RepositoryException if the subscriptions could not be removed.
481: * @see #subscribe(String,Object[],String,Date,Date,int,String[],WorkflowContext)
482: * @see #subscribe(String, Date, String[])
483: * @see #subscribe(String, Date, Date, int, Duration, String, boolean, String[])
484: */
485: void unsubscribe(String[] correlationKeys, boolean exact)
486: throws RepositoryException;
487: }
|