001: package org.obe.server.j2ee.ejb;
002:
003: import org.apache.commons.logging.Log;
004: import org.obe.OBERuntimeException;
005: import org.obe.engine.util.Comparators;
006: import org.obe.server.j2ee.J2EEServerConfig;
007: import org.obe.server.util.ObjectUtil;
008: import org.obe.spi.service.ApplicationEventBroker;
009: import org.obe.spi.service.ServerConfig;
010: import org.obe.spi.util.ApplicationEventUtil;
011: import org.obe.xpdl.model.misc.Duration;
012:
013: import javax.ejb.*;
014: import javax.ejb.Timer;
015: import java.util.*;
016:
017: /**
018: * Stores a persistent event subscription.
019: *
020: * @author Adrian Price
021: * @ejb:bean type="CMP"
022: * cmp-version="2.x"
023: * name="EventSubscription"
024: * display-name="OBE Application Event Subscription"
025: * jndi-name="org/obe/ejb/EventSubscription"
026: * local-jndi-name="org/obe/ejb/EventSubscriptionLocal"
027: * primkey-field="subscriptionId"
028: * reentrant="True"
029: * schema="EVENTSUBSCRIPTION"
030: * transaction-type="Container"
031: * view-type="local"
032: * @ejb:home local-class="org.obe.server.j2ee.ejb.EventSubscriptionLocalHome"
033: * local-extends="javax.ejb.EJBLocalHome"
034: * @ejb:interface local-class="org.obe.server.j2ee.ejb.EventSubscriptionLocal"
035: * local-extends="org.obe.event.AbstractApplicationEventBroker.ApplicationEventSubscription, org.obe.spi.service.ApplicationEventBroker.TemporalEventSubscription, javax.ejb.EJBLocalObject"
036: * @ejb:persistence table-name="OBEEVENTSUBSCRIPTION"
037: * @ejb:pk class="java.lang.Long"
038: * unchecked="true"
039: * @ejb:permission unchecked="true"
040: * @ejb:transaction type="Supports"
041: * @weblogic:transaction-isolation ${transaction.isolation}
042: * @ejb:finder signature="java.util.Collection findByEventHash(java.lang.String eventType, int eventKeyHashcode)"
043: * query="SELECT OBJECT(s) FROM EVENTSUBSCRIPTION AS s WHERE s.eventType=?1 AND s.eventKeyHashcode=?2 ORDER BY s.subscriptionId"
044: * result-type-mapping="Local"
045: * unchecked="true"
046: * @ejb:finder signature="java.util.Collection findByCorrelationKeys(java.lang.String key1)"
047: * query="SELECT OBJECT(s) FROM EVENTSUBSCRIPTION AS s WHERE s.correlationKey1=?1 ORDER BY s.subscriptionId"
048: * result-type-mapping="Local"
049: * unchecked="true"
050: * @ejb:finder signature="java.util.Collection findByCorrelationKeys(java.lang.String key1, java.lang.String key2)"
051: * query="SELECT OBJECT(s) FROM EVENTSUBSCRIPTION AS s WHERE s.correlationKey1=?1 AND s.correlationKey2=?2 ORDER BY s.subscriptionId"
052: * result-type-mapping="Local"
053: * unchecked="true"
054: * @ejb:finder signature="java.util.Collection findByCorrelationKeys(java.lang.String key1, java.lang.String key2, java.lang.String key3)"
055: * query="SELECT OBJECT(s) FROM EVENTSUBSCRIPTION AS s WHERE s.correlationKey1=?1 AND s.correlationKey2=?2 AND s.correlationKey3=?3 ORDER BY s.subscriptionId"
056: * result-type-mapping="Local"
057: * unchecked="true"
058: * @ejb:finder signature="java.util.Collection findByCorrelationKeys(java.lang.String key1, java.lang.String key2, java.lang.String key3, java.lang.String key4)"
059: * query="SELECT OBJECT(s) FROM EVENTSUBSCRIPTION AS s WHERE s.correlationKey1=?1 AND s.correlationKey2=?2 AND s.correlationKey3=?3 AND s.correlationKey4=?4 ORDER BY s.subscriptionId"
060: * result-type-mapping="Local"
061: * unchecked="true"
062: * @ejb:finder signature="java.util.Collection findByExactCorrelationKeys(java.lang.String key1)"
063: * query="SELECT OBJECT(s) FROM EVENTSUBSCRIPTION AS s WHERE s.correlationKey1=?1 AND s.correlationKeyCount=1 ORDER BY s.subscriptionId"
064: * result-type-mapping="Local"
065: * unchecked="true"
066: * @ejb:finder signature="java.util.Collection findByExactCorrelationKeys(java.lang.String key1, java.lang.String key2)"
067: * query="SELECT OBJECT(s) FROM EVENTSUBSCRIPTION AS s WHERE s.correlationKey1=?1 AND s.correlationKey2=?2 AND s.correlationKeyCount=2 ORDER BY s.subscriptionId"
068: * result-type-mapping="Local"
069: * unchecked="true"
070: * @ejb:finder signature="java.util.Collection findByExactCorrelationKeys(java.lang.String key1, java.lang.String key2, java.lang.String key3)"
071: * query="SELECT OBJECT(s) FROM EVENTSUBSCRIPTION AS s WHERE s.correlationKey1=?1 AND s.correlationKey2=?2 AND s.correlationKey3=?3 AND s.correlationKeyCount=3 ORDER BY s.subscriptionId"
072: * result-type-mapping="Local"
073: * unchecked="true"
074: * @ejb:finder signature="java.util.Collection findByExactCorrelationKeys(java.lang.String key1, java.lang.String key2, java.lang.String key3, java.lang.String key4)"
075: * query="SELECT OBJECT(s) FROM EVENTSUBSCRIPTION AS s WHERE s.correlationKey1=?1 AND s.correlationKey2=?2 AND s.correlationKey3=?3 AND s.correlationKey4=?4 AND s.correlationKeyCount=4 ORDER BY s.subscriptionId"
076: * result-type-mapping="Local"
077: * unchecked="true"
078: * @ejb:resource-ref res-name="jdbc/TxDataSource"
079: * res-type="javax.sql.DataSource"
080: * res-auth="Container"
081: * @jboss:resource-manager res-man-class="javax.sql.DataSource"
082: * res-man-name="jdbc/TxDataSource"
083: * res-man-jndi-name="java:/${xdoclet.DataSource}"
084: * @weblogic:resource-description res-ref-name="jdbc/TxDataSource"
085: * jndi-name="${xdoclet.DataSource}"
086: * @weblogic:data-source-name java:comp/env/jdbc/TxDataSource
087: * @jboss:create-table false
088: * @weblogic:pool max-beans-in-free-pool="225"
089: * initial-beans-in-free-pool="0"
090: * @weblogic:cache max-beans-in-cache="225"
091: * idle-timeout-seconds="0"
092: * concurrency-strategy="Database"
093: * @weblogic:persistence delay-updates-until-end-of-tx="True"
094: * @jboss:tuned-updates tune="true"
095: * @jboss:container-configuration name="${xdoclet.jboss.container-configuration}"
096: */
097: public abstract class EventSubscriptionEJB extends AbstractEntityEJB
098: implements TimedObject {
099:
100: private static final Log _logger = getLogger(EventSubscriptionEJB.class);
101:
102: private transient String[] _correlationKeys;
103:
104: /**
105: * @ejb:persistence column-name="SUBSCRIPTIONID"
106: */
107: public abstract Long getSubscriptionId();
108:
109: /**
110: * @ejb:persistence column-name="SUBSCRIPTIONID"
111: */
112: public abstract void setSubscriptionId(Long subscriptionId);
113:
114: /**
115: * @ejb:interface-method
116: * @ejb:persistence column-name="EVENTTYPE"
117: */
118: public abstract String getEventType();
119:
120: /**
121: * @ejb:persistence column-name="EVENTTYPE"
122: */
123: public abstract void setEventType(String eventType);
124:
125: /**
126: * @ejb:interface-method
127: * @ejb:persistence column-name="EVENTKEYS"
128: */
129: public abstract Object[] getEventKeys();
130:
131: /**
132: * @ejb:persistence column-name="EVENTKEYS"
133: */
134: public abstract void setEventKeys(Object[] eventKeys);
135:
136: /**
137: * @ejb:persistence column-name="EVENTKEYHASHCODE"
138: */
139: public abstract int getEventKeyHashcode();
140:
141: /**
142: * @ejb:persistence column-name="EVENTKEYHASHCODE"
143: */
144: public abstract void setEventKeyHashcode(int hashcode);
145:
146: /**
147: * @ejb:interface-method
148: * @ejb:persistence column-name="PREDICATE"
149: */
150: public abstract String getPredicate();
151:
152: /**
153: * @ejb:persistence column-name="PREDICATE"
154: */
155: public abstract void setPredicate(String condition);
156:
157: /**
158: * @ejb:persistence column-name="CONDITIONTYPE"
159: */
160: public abstract int getConditionType();
161:
162: /**
163: * @ejb:persistence column-name="CONDITIONTYPE"
164: */
165: public abstract void setConditionType(int conditionType);
166:
167: /**
168: * @ejb:interface-method
169: * @ejb:persistence column-name="EFFECTIVEDATE"
170: */
171: public abstract Date getEffective();
172:
173: /**
174: * @ejb:persistence column-name="EFFECTIVEDATE"
175: */
176: public abstract void setEffective(Date effective);
177:
178: /**
179: * @ejb:interface-method
180: * @ejb:persistence column-name="EXPIRYDATE"
181: */
182: public abstract Date getExpiry();
183:
184: /**
185: * @ejb:persistence column-name="EXPIRYDATE"
186: */
187: public abstract void setExpiry(Date expiry);
188:
189: /**
190: * @ejb:interface-method
191: * @ejb:persistence column-name="EVENTCOUNT"
192: */
193: public abstract int getCount();
194:
195: /**
196: * @ejb:persistence column-name="EVENTCOUNT"
197: */
198: public abstract void setCount(int count);
199:
200: /**
201: * @ejb:persistence column-name="RESCHEDINTERVAL"
202: */
203: public abstract String getIntervalStr();
204:
205: /**
206: * @ejb:persistence column-name="RESCHEDINTERVAL"
207: */
208: public abstract void setIntervalStr(String interval);
209:
210: /**
211: * @ejb:interface-method
212: * @ejb:persistence column-name="CALENDAR"
213: */
214: public abstract String getCalendar();
215:
216: /**
217: * @ejb:persistence column-name="CALENDAR"
218: */
219: public abstract void setCalendar(String calendar);
220:
221: /**
222: * @ejb:persistence column-name="RECOVERABLE"
223: */
224: public abstract boolean getRecoverable();
225:
226: /**
227: * @ejb:persistence column-name="RECOVERABLE"
228: */
229: public abstract void setRecoverable(boolean recoverable);
230:
231: /**
232: * @ejb:persistence column-name="CORRELATIONKEY1"
233: */
234: public abstract String getCorrelationKey1();
235:
236: /**
237: * @ejb:persistence column-name="CORRELATIONKEY1"
238: */
239: public abstract void setCorrelationKey1(String key);
240:
241: /**
242: * @ejb:persistence column-name="CORRELATIONKEY2"
243: */
244: public abstract String getCorrelationKey2();
245:
246: /**
247: * @ejb:persistence column-name="CORRELATIONKEY2"
248: */
249: public abstract void setCorrelationKey2(String key);
250:
251: /**
252: * @ejb:persistence column-name="CORRELATIONKEY3"
253: */
254: public abstract String getCorrelationKey3();
255:
256: /**
257: * @ejb:persistence column-name="CORRELATIONKEY3"
258: */
259: public abstract void setCorrelationKey3(String key);
260:
261: /**
262: * @ejb:persistence column-name="CORRELATIONKEY4"
263: */
264: public abstract String getCorrelationKey4();
265:
266: /**
267: * @ejb:persistence column-name="CORRELATIONKEY4"
268: */
269: public abstract void setCorrelationKey4(String key);
270:
271: /**
272: * @ejb:persistence column-name="CORRELATIONKEYCOUNT"
273: */
274: public abstract byte getCorrelationKeyCount();
275:
276: /**
277: * @ejb:persistence column-name="CORRELATIONKEYCOUNT"
278: */
279: public abstract void setCorrelationKeyCount(byte count);
280:
281: /**
282: * Retrieves subscriptions matching the specified event.
283: *
284: * @param eventType The event type.
285: * @param eventKeys The event keys.
286: * @param inputKeysHashcode The hashcode for the input event keys.
287: * @return A collection of matching subscriptions.
288: * @ejb:home-method
289: */
290: public Collection ejbHomeXfindByEvent(String eventType,
291: Object[] eventKeys, int inputKeysHashcode)
292: throws FinderException {
293:
294: // Look up potential matches by event type and event key hash.
295: EventSubscriptionLocalHome home = (EventSubscriptionLocalHome) _ctx
296: .getEJBLocalHome();
297: Collection candidates = home.findByEventHash(eventType,
298: inputKeysHashcode);
299:
300: // Select those subscriptions which actually match on each key.
301: long now = System.currentTimeMillis();
302: Collection subscriptions = new ArrayList(candidates.size());
303: for (Iterator i = candidates.iterator(); i.hasNext();) {
304: EventSubscriptionLocal candidate = (EventSubscriptionLocal) i
305: .next();
306: Date effective = candidate.getEffective();
307: Date expiry = candidate.getExpiry();
308: if ((effective == null || effective.getTime() <= now)
309: && (expiry == null || expiry.getTime() > now)
310: && Comparators.match(candidate.getEventKeys(),
311: eventKeys, false)) {
312:
313: subscriptions.add(candidate);
314: }
315: }
316: return subscriptions;
317: }
318:
319: /**
320: * Retrieves subscriptions matching the specified correlation keys.
321: *
322: * @param keys Correlation keys required by the process engine.
323: * The array is assumed to be ordered on ascending selectivity. If the
324: * array contains fewer elements than the original subscription, this
325: * implies that all subscriptions which match the supplied correlation
326: * key(s) should be selected.
327: * @param exact If <code>true</code>, returns only subscriptions which match
328: * on both correlation key values <i>and</i> count. If <code>false</code>,
329: * returns subscriptions with correlation keys for which <code>keys</code>
330: * is a contiguous subset.
331: * @return A collection of matching subscriptions.
332: * @throws FinderException
333: * @ejb:home-method
334: */
335: public Collection ejbHomeXfindByCorrelationKeys(String[] keys,
336: boolean exact) throws FinderException {
337:
338: if (keys == null) {
339: throw new IllegalArgumentException(
340: "correlationKeys cannot be null");
341: }
342: if (keys.length == 0) {
343: throw new IllegalArgumentException(
344: "correlationKeys cannot be empty");
345: }
346: if (keys.length > 4) {
347: throw new IllegalArgumentException(
348: "This implementation supports a maximum of four correlation keys");
349: }
350:
351: EventSubscriptionLocalHome home = (EventSubscriptionLocalHome) _ctx
352: .getEJBLocalHome();
353: if (exact) {
354: switch (keys.length) {
355: case 1:
356: return home.findByExactCorrelationKeys(keys[0]);
357: case 2:
358: return home
359: .findByExactCorrelationKeys(keys[0], keys[1]);
360: case 3:
361: return home.findByExactCorrelationKeys(keys[0],
362: keys[1], keys[2]);
363: case 4:
364: return home.findByExactCorrelationKeys(keys[0],
365: keys[1], keys[2], keys[3]);
366: }
367: } else {
368: switch (keys.length) {
369: case 1:
370: return home.findByCorrelationKeys(keys[0]);
371: case 2:
372: return home.findByCorrelationKeys(keys[0], keys[1]);
373: case 3:
374: return home.findByCorrelationKeys(keys[0], keys[1],
375: keys[2]);
376: case 4:
377: return home.findByCorrelationKeys(keys[0], keys[1],
378: keys[2], keys[3]);
379: }
380: }
381: return Collections.EMPTY_LIST;
382: }
383:
384: /**
385: * @ejb:create-method
386: */
387: public Long ejbCreate(String eventType, Object[] eventKeys,
388: String predicate, Date effective, Date expiry, int count,
389: String[] correlationKeys) throws CreateException {
390:
391: ApplicationEventUtil.validateSubscription(eventType, effective,
392: expiry, count, correlationKeys);
393:
394: // TODO: list all fields.
395: if (_logger.isDebugEnabled() && ServerConfig.isVerbose()) {
396: _logger.debug("ejbCreate(eventType=" + eventType
397: + ", eventKeys=" + Arrays.asList(eventKeys)
398: + ", correlationKeys="
399: + Arrays.asList(correlationKeys) + ')');
400: }
401:
402: clear();
403:
404: setSubscriptionId(new Long(EventSubscriptionDAO.getInstance()
405: .getNewId()));
406: setEventType(eventType);
407: setEventKeys(eventKeys);
408: setEventKeyHashcode(ObjectUtil.hashcode(eventKeys));
409: setPredicate(predicate);
410: setEffective(effective);
411: setExpiry(expiry);
412: setCount(count);
413: setCorrelationKeys(correlationKeys);
414:
415: return null;
416: }
417:
418: public void ejbPostCreate(String eventType, Object[] eventKeys,
419: String predicate, Date effective, Date expiry, int count,
420: String[] correlationKeys) throws CreateException {
421:
422: // TODO: list all fields.
423: if (_logger.isDebugEnabled() && ServerConfig.isVerbose()) {
424: _logger.debug("ejbPostCreate(eventType=" + eventType
425: + ", eventKeys=" + Arrays.asList(eventKeys)
426: + ", predicate=" + predicate + ", correlationKeys="
427: + Arrays.asList(correlationKeys) + ')');
428: }
429:
430: // If this application event subscription has an expiry date, schedule
431: // a timed event to remove the subscription at that point.
432: if (expiry != null)
433: _ctx.getTimerService().createTimer(expiry, null);
434: }
435:
436: /**
437: * @ejb:create-method
438: */
439: public Long ejbCreate(String eventType, Date effective,
440: Date expiry, int count, String interval, String calendar,
441: boolean recoverable, String[] correlationKeys)
442: throws CreateException {
443:
444: ApplicationEventUtil.validateSubscription(eventType, effective,
445: expiry, count, Duration.valueOf(interval),
446: correlationKeys);
447:
448: // TODO: list all fields.
449: if (_logger.isDebugEnabled() && ServerConfig.isVerbose()) {
450: _logger.debug("ejbCreate(eventType=" + eventType
451: + ", date=" + effective + ", correlationKeys="
452: + Arrays.asList(correlationKeys) + ')');
453: }
454:
455: clear();
456:
457: setSubscriptionId(new Long(EventSubscriptionDAO.getInstance()
458: .getNewId()));
459: setEventType(eventType);
460: setEventKeyHashcode(0);
461: setEffective(effective);
462: setExpiry(expiry);
463: setCount(count);
464: setIntervalStr(interval);
465: setCalendar(calendar);
466: setRecoverable(recoverable);
467: setCorrelationKeys(correlationKeys);
468:
469: return null;
470: }
471:
472: public void ejbPostCreate(String eventType, Date effective,
473: Date expiry, int count, String interval, String calendar,
474: boolean recoverable, String[] correlationKeys)
475: throws CreateException {
476:
477: // TODO: list all fields.
478: if (_logger.isDebugEnabled() && ServerConfig.isVerbose()) {
479: _logger.debug("ejbPostCreate(eventType=" + eventType
480: + ", date=" + effective + ", correlationKeys="
481: + Arrays.asList(correlationKeys) + ')');
482: }
483:
484: // Schedule a timed event so that we can notify listeners at that point.
485: if (effective != null)
486: _ctx.getTimerService().createTimer(effective, effective);
487: }
488:
489: public void ejbTimeout(Timer timer) {
490: // Determine what kind of timeout this is (effective or expiry).
491: Date last = (Date) timer.getInfo();
492: if (last != null) {
493: // This is the timeout event for a temporal event subscription's
494: // effective date. Fire the event and reschedule as appropriate.
495: Date next = ApplicationEventUtil
496: .handleTimeout(
497: (ApplicationEventBroker.TemporalEventSubscription) _ctx
498: .getEJBLocalObject(), last,
499: J2EEServerConfig.svcMgr);
500: if (next != null)
501: _ctx.getTimerService().createTimer(next, next);
502: } else {
503: try {
504: _ctx.getEJBLocalObject().remove();
505: } catch (RemoveException e) {
506: // Nothing much we can do here other than log the exception.
507: _logger.error("Unable to cancel event subscription", e);
508: }
509: }
510: }
511:
512: protected Log getLogger() {
513: return _logger;
514: }
515:
516: protected void clear() {
517: super .clear();
518: _correlationKeys = null;
519: }
520:
521: /**
522: * @ejb:interface-method
523: */
524: public Duration getInterval() {
525: return Duration.valueOf(getIntervalStr());
526: }
527:
528: /**
529: * @ejb:interface-method
530: */
531: public boolean isRecoverable() {
532: return getRecoverable();
533: }
534:
535: /**
536: * @ejb:interface-method
537: */
538: public String[] getCorrelationKeys() {
539: if (_correlationKeys == null) {
540: int count = getCorrelationKeyCount();
541: _correlationKeys = new String[count];
542: switch (count) {
543: case 4:
544: _correlationKeys[3] = getCorrelationKey4();
545: case 3:
546: _correlationKeys[2] = getCorrelationKey3();
547: case 2:
548: _correlationKeys[1] = getCorrelationKey2();
549: case 1:
550: _correlationKeys[0] = getCorrelationKey1();
551: }
552: }
553: return _correlationKeys;
554: }
555:
556: private void setCorrelationKeys(String[] correlationKeys) {
557: int count = correlationKeys.length;
558: switch (count) {
559: case 4:
560: setCorrelationKey4(correlationKeys[3]);
561: case 3:
562: setCorrelationKey3(correlationKeys[2]);
563: case 2:
564: setCorrelationKey2(correlationKeys[1]);
565: case 1:
566: setCorrelationKey1(correlationKeys[0]);
567: case 0:
568: break;
569: default:
570: // Shouldn't ever happen since the correlation keys are
571: // supplied by the OBE runtime.
572: throw new IllegalArgumentException(
573: "This implementation supports a maximum of four correlation keys");
574: }
575: setCorrelationKeyCount((byte) count);
576: }
577:
578: /**
579: * @ejb:interface-method
580: */
581: public int decrementCount() {
582: int count = getCount();
583: if (count > 0)
584: setCount(--count);
585: return count;
586: }
587:
588: /**
589: * @ejb:interface-method
590: */
591: public void cancel() {
592: try {
593: _ctx.getEJBLocalObject().remove();
594: } catch (RemoveException e) {
595: throw new OBERuntimeException(e);
596: }
597: }
598: }
|