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.server.j2ee.repository;
046:
047: import org.apache.commons.logging.Log;
048: import org.obe.client.api.repository.RepositoryException;
049: import org.obe.spi.model.*;
050: import org.obe.spi.service.ServerConfig;
051: import org.wfmc.wapi.WMActivityInstanceState;
052: import org.wfmc.wapi.WMFilter;
053:
054: import javax.ejb.*;
055: import java.io.Serializable;
056: import java.sql.SQLException;
057: import java.util.*;
058:
059: /**
060: * Holds the persistent state of an activity instance.
061: *
062: * @author Adrian Price
063: * @ejb:bean type="CMP"
064: * cmp-version="2.x"
065: * name="ActivityInstance"
066: * display-name="OBE Activity Instance"
067: * jndi-name="org/obe/ejb/ActivityInstance"
068: * local-jndi-name="org/obe/ejb/ActivityInstanceLocal"
069: * primkey-field="activityInstanceId"
070: * reentrant="False"
071: * schema="ACTIVITYINSTANCE"
072: * transaction-type="Container"
073: * view-type="local"
074: * @ejb:home local-class="org.obe.server.j2ee.repository.ActivityInstanceLocalHome"
075: * local-extends="javax.ejb.EJBLocalHome"
076: * @ejb:interface local-class="org.obe.server.j2ee.repository.ActivityInstanceLocal"
077: * local-extends="org.obe.spi.model.ActivityInstance, javax.ejb.EJBLocalObject"
078: * @ejb:persistence table-name="OBEACTIVITYINSTANCE"
079: * @ejb:pk class="java.lang.String"
080: * unchecked="true"
081: * @ejb:permission unchecked="true"
082: * @ejb:transaction type="Supports"
083: * @weblogic:transaction-isolation ${transaction.isolation}
084: * @ejb:finder signature="java.util.Collection findAll()"
085: * query="SELECT OBJECT(a) FROM ACTIVITYINSTANCE AS a"
086: * result-type-mapping="Local"
087: * unchecked="true"
088: * @ejb:finder signature="java.util.Collection findByProcessDefinitionId(java.lang.String processDefinitionId)"
089: * query="SELECT OBJECT(a) FROM ACTIVITYINSTANCE AS a WHERE a.processDefinitionId=?1"
090: * result-type-mapping="Local"
091: * unchecked="true"
092: * @ejb:finder signature="org.obe.server.j2ee.repository.ActivityInstanceLocal findByActivityDefinitionId(java.lang.String processInstanceId, java.lang.String activityDefinitionId, java.lang.String blockActivityInstanceId)"
093: * query="SELECT OBJECT(a) FROM ACTIVITYINSTANCE AS a WHERE a.processInstanceIdCmp=?1 AND a.blockActivityInstanceId=?3 AND a.activityDefinitionId=?2"
094: * result-type-mapping="Local"
095: * unchecked="true"
096: * @ejb:finder signature="java.util.Collection findByProcessActivityDefinitionId(java.lang.String processDefinitionId, java.lang.String activityDefinitionId)"
097: * query="SELECT OBJECT(a) FROM ACTIVITYINSTANCE AS a WHERE a.processDefinitionId=?1 AND a.activityDefinitionId=?2"
098: * result-type-mapping="Local"
099: * unchecked="true"
100: * @ejb:ejb-ref ejb-name="Sequence"
101: * view-type="local"
102: * @weblogic:ejb-local-reference-description ejb-ref-name="ejb/Sequence"
103: * jndi-name="org/obe/ejb/SequenceLocal"
104: * @ejb:ejb-ref ejb-name="ProcessInstance"
105: * view-type="local"
106: * @weblogic:ejb-local-reference-description ejb-ref-name="ejb/ProcessInstance"
107: * jndi-name="org/obe/ejb/ProcessInstanceLocal"
108: * @ejb:ejb-ref ejb-name="WorkItem"
109: * view-type="local"
110: * @weblogic:ejb-local-reference-description ejb-ref-name="ejb/WorkItem"
111: * jndi-name="org/obe/ejb/WorkItemLocal"
112: * @ejb:resource-ref res-name="jdbc/TxDataSource"
113: * res-type="javax.sql.DataSource"
114: * res-auth="Container"
115: * @jboss:resource-manager res-man-class="javax.sql.DataSource"
116: * res-man-name="jdbc/TxDataSource"
117: * res-man-jndi-name="java:/${xdoclet.DataSource}"
118: * @weblogic:resource-description res-ref-name="jdbc/TxDataSource"
119: * jndi-name="${xdoclet.DataSource}"
120: * @weblogic:data-source-name java:comp/env/jdbc/TxDataSource
121: * @jboss:create-table false
122: * @weblogic:pool max-beans-in-free-pool="225"
123: * initial-beans-in-free-pool="0"
124: * @weblogic:cache max-beans-in-cache="225"
125: * idle-timeout-seconds="0"
126: * concurrency-strategy="Database"
127: * @weblogic:persistence delay-updates-until-end-of-tx="True"
128: * @jboss:tuned-updates tune="true"
129: * @jboss:container-configuration name="${xdoclet.jboss.container-configuration}"
130: * @jboss:cache-invalidation value="True"
131: * @jboss:cache-invalidation-config invalidation-group-name="ActivityInstance"
132: */
133: public abstract class ActivityInstanceEJB extends
134: AbstractAttributedEntityEJB {
135: private static final long serialVersionUID = 6021616302636604129L;
136: private static final Log _logger = getLogger(ActivityInstanceEJB.class);
137:
138: // This serializable class holds the internal state of the activity.
139: protected static final class ActivitySerialData implements
140: Serializable {
141: private static final long serialVersionUID = -8728650539468487203L;
142: final JoinInstance join;
143: final PersistentIterator blockActivityIterator;
144:
145: ActivitySerialData(JoinInstance join,
146: PersistentIterator blockActivityIterator) {
147:
148: this .join = join;
149: this .blockActivityIterator = blockActivityIterator;
150: }
151:
152: boolean isModified() {
153: return join != null && join.isModified()
154: || blockActivityIterator != null
155: && blockActivityIterator.isModified();
156: }
157: }
158:
159: public ActivityInstanceEJB() {
160: super (ActivityInstance.propertyDescriptors);
161: }
162:
163: /**
164: * @ejb:create-method
165: */
166: public String ejbCreate(String processDefinitionId,
167: String activityDefinitionId, String activityName,
168: JoinInstance join, String blockActivityInstanceId,
169: PersistentIterator blockActivityIterator, int priority,
170: int state, String[] participants) throws CreateException,
171: RepositoryException {
172:
173: if (_logger.isDebugEnabled() && ServerConfig.isVerbose()) {
174: _logger.debug("ejbCreate(" + processDefinitionId + ", "
175: + activityDefinitionId + ", " + activityName + ", "
176: + join + ", " + blockActivityInstanceId + ", "
177: + blockActivityIterator + ", " + priority + ", "
178: + state + ", " + participants + ')');
179: }
180:
181: clear();
182:
183: // When a cmp-field and a cmr-field (relationship) are mapped to the
184: // same column, the setXXX method for the cmp-field may not be called.
185: // The cmp-field is read-only.
186: // setProcessInstanceId(processInstanceId);
187: setActivityDefinitionId(activityDefinitionId);
188: setActivityInstanceId(ActivityInstanceDAO.getInstance()
189: .getNewId());
190: setBlockActivityInstanceId(blockActivityInstanceId);
191: setName(activityName);
192: setParticipants(participants);
193: setProcessDefinitionId(processDefinitionId);
194: setPriority(priority);
195: setSerialDataCmp(new ActivitySerialData(join,
196: blockActivityIterator));
197: setState(state);
198: // setToolIndex(-1);
199:
200: return null;
201: }
202:
203: public void ejbPostCreate(String processDefinitionId,
204: String activityDefinitionId, String activityName,
205: JoinInstance join, String blockActivityInstanceId,
206: PersistentIterator blockActivityIterator, int priority,
207: int state, String[] participants) throws CreateException,
208: RepositoryException {
209:
210: if (_logger.isDebugEnabled() && ServerConfig.isVerbose()) {
211: _logger.debug("ejbPostCreate(pk=" + _ctx.getPrimaryKey()
212: + ", " + processDefinitionId + ", "
213: + activityDefinitionId + ", " + activityName + ", "
214: + join + ", " + blockActivityInstanceId + ", "
215: + blockActivityIterator + ", " + priority + ", "
216: + state + ", " + participants + ", " + ')');
217: }
218: _ejbLocalObject = _ctx.getEJBLocalObject();
219: }
220:
221: /**
222: * Counts the number of matching activity instances.
223: *
224: * @param filter Activity instance filter; can be <code>null</code>.
225: * @return The number of matching activity instances.
226: * @ejb:home-method
227: */
228: public int ejbHomeCount(WMFilter filter) throws RepositoryException {
229: try {
230: return ActivityInstanceDAO.getInstance().count(filter);
231: } catch (SQLException e) {
232: throw new RepositoryException(e);
233: }
234: }
235:
236: /**
237: * Retrieves matching activity instances.
238: *
239: * @param processDefinitionId The process definition id, can be
240: * <code>null</code>.
241: * @param filter Activity instance filter; should not be <code>null</code>
242: * (use {@link ActivityInstanceLocalHome#findAll} instead).
243: * @return A collection of matching activity instances.
244: * @ejb:home-method
245: * @see ActivityInstanceLocalHome#findAll
246: */
247: public Collection ejbHomeXfindByFilter(String processDefinitionId,
248: String activityDefinitionId, WMFilter filter)
249: throws FinderException, RepositoryException {
250:
251: try {
252: List activities = ActivityInstanceDAO.getInstance()
253: .findByFilter(processDefinitionId,
254: activityDefinitionId, filter);
255: ActivityInstanceLocalHome home = (ActivityInstanceLocalHome) _ctx
256: .getEJBLocalHome();
257: for (int i = 0; i < activities.size(); i++) {
258: try {
259: activities.set(i, home
260: .findByPrimaryKey((String) activities
261: .get(i)));
262: } catch (ObjectNotFoundException e) {
263: // Someone else deleted this PK, so just skip it.
264: activities.remove(i--);
265: }
266: }
267: return activities;
268: } catch (SQLException e) {
269: throw new RepositoryException(e);
270: }
271: }
272:
273: // Override ejbLoad to remove the call to clear().
274: // Weblogic8.1 calls ejbLoad frequently, destroying
275: // the _workItems and _childProcessInstances collections
276: // while being used by other code during iteration.
277: // This causes ConcurrentModificationException failure.
278: // TODO - Is it worth caching these collections given
279: // the potential risk of concurrent access?
280: public void ejbLoad() {
281: if (getLogger().isDebugEnabled() && ServerConfig.isVerbose())
282: getLogger().debug(
283: "ejbLoad(pk=" + _ctx.getPrimaryKey() + ')');
284: _ejbLocalObject = _ctx.getEJBLocalObject();
285: }
286:
287: public void ejbStore() {
288: super .ejbStore();
289:
290: // If any of the serializable data have changed, force them to
291: // be persisted by first clearing then setting the serialData CMP field.
292: // (Otherwise, CMP won't recognize the object as dirty, and we'll lose
293: // its state.)
294: ActivitySerialData serialData = getSerialData();
295: if (serialData.isModified()) {
296: setSerialDataCmp(null);
297: setSerialDataCmp(serialData);
298: // The following code is an alternative implementation for the line
299: // above that is necessary when running on JBoss-3.2.2 or earlier
300: // (N.B. not recommended). JBoss-3.2.2 persistence layer makes the
301: // erroneous assumption that serialized objects are immutable: if a
302: // mutable object is changed, JBoss will not persist the changes.
303: // [per Deng Wu]
304: /*
305: setSerialDataCmp(new ActivitySerialData(serialData.join,
306: serialData.blockActivityIterator));
307: */
308: }
309: }
310:
311: public void ejbRemove() throws RemoveException {
312: // Cascade delete to all work items associated with this instance.
313: // N.B. Must copy the work item collection because remove() alters it.
314: Collection coll = new ArrayList(getWorkItems());
315: for (Iterator iter = coll.iterator(); iter.hasNext();)
316: ((EJBLocalObject) iter.next()).remove();
317:
318: // Cascade delete to all attributes associated with this instance.
319: super .ejbRemove();
320: }
321:
322: /**
323: * @ejb:interface-method
324: * @ejb:persistence column-name="ACTIVITYINSTANCEID"
325: */
326: public abstract String getActivityInstanceId();
327:
328: /**
329: * @ejb:persistence column-name="ACTIVITYINSTANCEID"
330: */
331: public abstract void setActivityInstanceId(String activityInstanceId);
332:
333: /**
334: * @ejb:interface-method
335: * @ejb:persistence column-name="BLKACTIVITYINSTANCEID"
336: */
337: public abstract String getBlockActivityInstanceId();
338:
339: /**
340: * @ejb:persistence column-name="BLKACTIVITYINSTANCEID"
341: */
342: public abstract void setBlockActivityInstanceId(
343: String activityInstanceId);
344:
345: /**
346: * @ejb:interface-method
347: * @ejb:persistence column-name="ACTIVITYDEFINITIONID"
348: */
349: public abstract String getActivityDefinitionId();
350:
351: /**
352: * @ejb:persistence column-name="ACTIVITYDEFINITIONID"
353: */
354: public abstract void setActivityDefinitionId(
355: String activityDefinitionId);
356:
357: /**
358: * @ejb:interface-method
359: * @ejb:persistence column-name="STARTEDDATE"
360: */
361: public abstract Date getStartedDate();
362:
363: /**
364: * @ejb:interface-method
365: * @ejb:persistence column-name="STARTEDDATE"
366: */
367: public abstract void setStartedDate(Date startedDate);
368:
369: /**
370: * @ejb:interface-method
371: * @ejb:persistence column-name="COMPLETEDDATE"
372: */
373: public abstract Date getCompletedDate();
374:
375: /**
376: * @ejb:interface-method
377: * @ejb:persistence column-name="COMPLETEDDATE"
378: */
379: public abstract void setCompletedDate(Date completedDate);
380:
381: /**
382: * @ejb:interface-method
383: * @ejb:persistence column-name="TARGETDATE"
384: */
385: public abstract Date getTargetDate();
386:
387: /**
388: * @ejb:interface-method
389: * @ejb:persistence column-name="TARGETDATE"
390: */
391: public abstract void setTargetDate(Date targetDate);
392:
393: /**
394: * @ejb:interface-method
395: * @ejb:persistence column-name="DUEDATE"
396: */
397: public abstract Date getDueDate();
398:
399: /**
400: * @ejb:interface-method
401: * @ejb:persistence column-name="DUEDATE"
402: */
403: public abstract void setDueDate(Date dueDate);
404:
405: /**
406: * @ejb:persistence column-name="SERIALDATA"
407: */
408: public abstract Object getSerialDataCmp();
409:
410: /**
411: * @ejb:persistence column-name="SERIALDATA"
412: */
413: public abstract void setSerialDataCmp(Object serialData);
414:
415: private ActivitySerialData getSerialData() {
416: return (ActivitySerialData) getSerialDataCmp();
417: }
418:
419: /**
420: * @ejb:interface-method
421: */
422: public PersistentIterator getBlockActivityIterator() {
423: return getSerialData().blockActivityIterator;
424: }
425:
426: /**
427: * @ejb:interface-method
428: */
429: public JoinInstance getJoin() {
430: return getSerialData().join;
431: }
432:
433: /**
434: * @ejb:interface-method
435: * @ejb:persistence column-name="NAME"
436: */
437: public abstract String getName();
438:
439: /**
440: * @ejb:interface-method
441: * @ejb:persistence column-name="NAME"
442: */
443: public abstract void setName(String name);
444:
445: /**
446: * @ejb:interface-method
447: * @ejb:persistence column-name="PARTICIPANTS"
448: */
449: public abstract String[] getParticipants();
450:
451: /**
452: * @ejb:interface-method
453: * @ejb:persistence column-name="PARTICIPANTS"
454: */
455: public abstract void setParticipants(String[] participants);
456:
457: /**
458: * @ejb:interface-method
459: * @ejb:persistence column-name="PRIORITY"
460: */
461: public abstract int getPriority();
462:
463: /**
464: * @ejb:interface-method
465: * @ejb:persistence column-name="PRIORITY"
466: */
467: public abstract void setPriority(int priority);
468:
469: /**
470: * @ejb:interface-method
471: * @ejb:persistence column-name="STATE"
472: */
473: public abstract int getState();
474:
475: /**
476: * @ejb:interface-method
477: * @ejb:persistence column-name="STATE"
478: */
479: public abstract void setState(int state);
480:
481: /**
482: * @ejb:relation name="ProcessInstance-ActivityInstance"
483: * role-name="ActivityInstance-Has-A-ProcessInstance"
484: * cascade-delete="yes"
485: * @jboss:relation related-pk-field="processInstanceId"
486: * fk-column="PROCESSINSTANCEID"
487: * @weblogic:column-map foreign-key-column="PROCESSINSTANCEID"
488: * key-column="PROCESSINSTANCEID"
489: */
490: public abstract ProcessInstanceLocal getProcessInstanceCmr();
491:
492: public abstract void setProcessInstanceCmr(
493: ProcessInstanceLocal process);
494:
495: /**
496: * @ejb:interface-method
497: * @ejb:relation name="ActivityInstance-ChildProcessInstance"
498: * role-name="ActivityInstance-Has-ChildProcessInstances"
499: */
500: public abstract Collection getChildProcessInstances();
501:
502: public abstract void setChildProcessInstances(Collection processes);
503:
504: /**
505: * @ejb:interface-method
506: */
507: public ProcessInstance addChildProcessInstance(
508: String processDefinitionId, String instanceName,
509: int priority, int state, Date createdDate,
510: Date startedDate, String[] participants)
511: throws RepositoryException {
512:
513: try {
514: ProcessInstance processInstance = EJBLocalHelper
515: .getProcessInstanceHome().create(
516: processDefinitionId, instanceName,
517: priority, state, createdDate, startedDate,
518: participants);
519: getChildProcessInstances().add(processInstance);
520: return processInstance;
521: } catch (CreateException e) {
522: throw new RepositoryException(e);
523: }
524: }
525:
526: /**
527: * @ejb:interface-method
528: */
529: public ProcessInstance getProcessInstance() {
530: return getProcessInstanceCmr();
531: }
532:
533: /**
534: * @ejb:interface-method
535: */
536: public String getProcessInstanceId() {
537: return getProcessInstanceCmr().getProcessInstanceId();
538: }
539:
540: /**
541: * @ejb:persistence column-name="PROCESSINSTANCEID"
542: */
543: public abstract String getProcessInstanceIdCmp();
544:
545: /**
546: * @ejb:persistence column-name="PROCESSINSTANCEID"
547: */
548: public abstract void setProcessInstanceIdCmp(
549: String processInstanceId);
550:
551: /**
552: * @ejb:relation name="ActivityInstance-WorkItem"
553: * role-name="ActivityInstance-Has-WorkItems"
554: */
555: public abstract Collection getWorkItems();
556:
557: public abstract void setWorkItems(Collection workItems);
558:
559: /**
560: * @ejb:interface-method
561: */
562: public WorkItem addWorkItem(int toolIndex, int state,
563: String performer, String participant)
564: throws RepositoryException {
565:
566: try {
567: WorkItemLocal workItem = EJBLocalHelper.getWorkItemHome()
568: .create(getProcessDefinitionId(),
569: getProcessInstanceId(), toolIndex,
570: getName(), getPriority(), state, performer,
571: participant);
572: getWorkItems().add(workItem);
573: return workItem;
574: } catch (CreateException e) {
575: throw new RepositoryException(e);
576: }
577: }
578:
579: /**
580: * @ejb:interface-method
581: */
582: public String getEntityId() {
583: return getActivityInstanceId();
584: }
585:
586: /**
587: * @ejb:interface-method
588: */
589: public String toString() {
590: return "ActivityInstance[activityInstanceId='"
591: + getEntityId()
592: + "', activityDefinitionId='"
593: + getActivityDefinitionId()
594: + "', processInstanceId='"
595: + getProcessInstanceId()
596: + "', processDefinitionId='"
597: + getProcessDefinitionId()
598: + ", state="
599: + WMActivityInstanceState.valueOf(getState())
600: .stringValue()
601: + ", priority="
602: + getPriority()
603: + ", startedDate="
604: + getStartedDate()
605: + ", completedDate="
606: + getCompletedDate()
607: + ", dueDate="
608: + getDueDate()
609: + ", join="
610: + getJoin()
611: + ", name='"
612: + getName()
613: + "', blockActivityInstanceId="
614: + (getBlockActivityInstanceId() == null ? null
615: : '\'' + getBlockActivityInstanceId() + '\'')
616: + "', blockActivityIterator="
617: + (getBlockActivityIterator() == null ? null
618: : '\'' + getBlockActivityIterator().toString() + '\'')
619: + "']";
620: }
621:
622: protected int getOwnerType() {
623: return AttributedEntity.ACTIVITY_INSTANCE_TYPE;
624: }
625:
626: protected final Log getLogger() {
627: return _logger;
628: }
629: }
|