001: /*
002: * JBoss, Home of Professional Open Source
003: * Copyright 2005, JBoss Inc., and individual contributors as indicated
004: * by the @authors tag. See the copyright.txt in the distribution for a
005: * full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jbpm.db;
023:
024: import java.util.ArrayList;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.List;
028: import java.util.Map;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032: import org.hibernate.HibernateException;
033: import org.hibernate.LockMode;
034: import org.hibernate.Query;
035: import org.hibernate.Session;
036: import org.jbpm.JbpmException;
037: import org.jbpm.graph.def.ProcessDefinition;
038: import org.jbpm.graph.exe.ProcessInstance;
039: import org.jbpm.graph.exe.Token;
040:
041: /**
042: * are the graph related database operations.
043: */
044: public class GraphSession {
045:
046: JbpmSession jbpmSession = null;
047: Session session = null;
048:
049: public GraphSession(JbpmSession jbpmSession) {
050: this .jbpmSession = jbpmSession;
051: this .session = jbpmSession.getSession();
052: }
053:
054: public GraphSession(Session session) {
055: this .session = session;
056: this .jbpmSession = new JbpmSession(session);
057: }
058:
059: // process definitions //////////////////////////////////////////////////////
060:
061: public void deployProcessDefinition(
062: ProcessDefinition processDefinition) {
063: String processDefinitionName = processDefinition.getName();
064: // if the process definition has a name (process versioning only applies to named process definitions)
065: if (processDefinitionName != null) {
066: // find the current latest process definition
067: ProcessDefinition previousLatestVersion = findLatestProcessDefinition(processDefinitionName);
068: // if there is a current latest process definition
069: if (previousLatestVersion != null) {
070: // take the next version number
071: processDefinition.setVersion(previousLatestVersion
072: .getVersion() + 1);
073: } else {
074: // start from 1
075: processDefinition.setVersion(1);
076: }
077:
078: session.save(processDefinition);
079:
080: } else {
081: throw new JbpmException(
082: "process definition does not have a name");
083: }
084: }
085:
086: /**
087: * saves the process definitions. this method does not assign a version
088: * number. that is the responsibility of the {@link org.jbpm.jpdl.par.ProcessArchiveDeployer}.
089: */
090: public void saveProcessDefinition(
091: ProcessDefinition processDefinition) {
092: try {
093: session.save(processDefinition);
094: } catch (Exception e) {
095: e.printStackTrace();
096: log.error(e);
097: jbpmSession.handleException();
098: throw new JbpmException(
099: "couldn't save process definition '"
100: + processDefinition + "'", e);
101: }
102: }
103:
104: /**
105: * loads a process definition from the database by the identifier.
106: * @throws JbpmException in case the referenced process definition doesn't exist.
107: */
108: public ProcessDefinition loadProcessDefinition(
109: long processDefinitionId) {
110: try {
111: return (ProcessDefinition) session.load(
112: ProcessDefinition.class, new Long(
113: processDefinitionId));
114: } catch (Exception e) {
115: e.printStackTrace();
116: log.error(e);
117: jbpmSession.handleException();
118: throw new JbpmException(
119: "couldn't load process definition '"
120: + processDefinitionId + "'", e);
121: }
122: }
123:
124: /**
125: * gets a process definition from the database by the identifier.
126: * @return the referenced process definition or null in case it doesn't exist.
127: */
128: public ProcessDefinition getProcessDefinition(
129: long processDefinitionId) {
130: try {
131: return (ProcessDefinition) session.get(
132: ProcessDefinition.class, new Long(
133: processDefinitionId));
134: } catch (Exception e) {
135: e.printStackTrace();
136: log.error(e);
137: jbpmSession.handleException();
138: throw new JbpmException("couldn't get process definition '"
139: + processDefinitionId + "'", e);
140: }
141: }
142:
143: /**
144: * queries the database for a process definition with the given name and version.
145: */
146: public ProcessDefinition findProcessDefinition(String name,
147: int version) {
148: ProcessDefinition processDefinition = null;
149: try {
150: Query query = session
151: .getNamedQuery("GraphSession.findProcessDefinitionByNameAndVersion");
152: query.setString("name", name);
153: query.setInteger("version", version);
154: processDefinition = (ProcessDefinition) query
155: .uniqueResult();
156: } catch (Exception e) {
157: e.printStackTrace();
158: log.error(e);
159: jbpmSession.handleException();
160: throw new JbpmException(
161: "couldn't get process definition with name '"
162: + name + "' and version '" + version + "'",
163: e);
164: }
165: return processDefinition;
166: }
167:
168: /**
169: * queries the database for the latest version of a process definition with the given name.
170: */
171: public ProcessDefinition findLatestProcessDefinition(String name) {
172: ProcessDefinition processDefinition = null;
173: try {
174: Query query = session
175: .getNamedQuery("GraphSession.findLatestProcessDefinitionQuery");
176: query.setString("name", name);
177: query.setMaxResults(1);
178: processDefinition = (ProcessDefinition) query
179: .uniqueResult();
180: } catch (Exception e) {
181:
182: System.out.flush();
183: System.err.flush();
184: try {
185: Thread.sleep(200);
186: } catch (InterruptedException e1) {
187: e1.printStackTrace();
188: }
189:
190: e.printStackTrace();
191: log.error(e);
192:
193: System.out.flush();
194: System.err.flush();
195:
196: jbpmSession.handleException();
197: throw new JbpmException(
198: "couldn't find process definition '" + name + "'",
199: e);
200: }
201: return processDefinition;
202: }
203:
204: /**
205: * queries the database for the latest version of each process definition.
206: * Process definitions are distinct by name.
207: */
208: public List findLatestProcessDefinitions() {
209: List processDefinitions = new ArrayList();
210: Map processDefinitionsByName = new HashMap();
211: try {
212: Query query = session
213: .getNamedQuery("GraphSession.findAllProcessDefinitions");
214: Iterator iter = query.list().iterator();
215: while (iter.hasNext()) {
216: ProcessDefinition processDefinition = (ProcessDefinition) iter
217: .next();
218: String processDefinitionName = processDefinition
219: .getName();
220: ProcessDefinition previous = (ProcessDefinition) processDefinitionsByName
221: .get(processDefinitionName);
222: if ((previous == null)
223: || (previous.getVersion() < processDefinition
224: .getVersion())) {
225: processDefinitionsByName.put(processDefinitionName,
226: processDefinition);
227: }
228: }
229: processDefinitions = new ArrayList(processDefinitionsByName
230: .values());
231: } catch (Exception e) {
232: e.printStackTrace();
233: log.error(e);
234: jbpmSession.handleException();
235: throw new JbpmException(
236: "couldn't find latest versions of process definitions",
237: e);
238: }
239: return processDefinitions;
240: }
241:
242: /**
243: * queries the database for all process definitions, ordered by name (ascending), then by version (descending).
244: */
245: public List findAllProcessDefinitions() {
246: try {
247: Query query = session
248: .getNamedQuery("GraphSession.findAllProcessDefinitions");
249: return query.list();
250: } catch (Exception e) {
251: e.printStackTrace();
252: log.error(e);
253: jbpmSession.handleException();
254: throw new JbpmException(
255: "couldn't find all process definitions", e);
256: }
257: }
258:
259: /**
260: * queries the database for all versions of process definitions with the given name, ordered by version (descending).
261: */
262: public List findAllProcessDefinitionVersions(String name) {
263: try {
264: Query query = session
265: .getNamedQuery("GraphSession.findAllProcessDefinitionVersions");
266: query.setString("name", name);
267: return query.list();
268: } catch (HibernateException e) {
269: e.printStackTrace();
270: log.error(e);
271: throw new JbpmException(
272: "couldn't find all versions of process definition '"
273: + name + "'", e);
274: }
275: }
276:
277: public void deleteProcessDefinition(long processDefinitionId) {
278: deleteProcessDefinition(loadProcessDefinition(processDefinitionId));
279: }
280:
281: public void deleteProcessDefinition(
282: ProcessDefinition processDefinition) {
283: if (processDefinition == null)
284: throw new JbpmException(
285: "processDefinition is null in JbpmSession.deleteProcessDefinition()");
286: try {
287: // delete all the process instances of this definition
288: List processInstances = findProcessInstances(processDefinition
289: .getId());
290: if (processInstances != null) {
291: Iterator iter = processInstances.iterator();
292: while (iter.hasNext()) {
293: deleteProcessInstance((ProcessInstance) iter.next());
294: }
295: }
296:
297: // then delete the process definition
298: session.delete(processDefinition);
299:
300: } catch (Exception e) {
301: e.printStackTrace();
302: log.error(e);
303: jbpmSession.handleException();
304: throw new JbpmException(
305: "couldn't delete process definition '"
306: + processDefinition.getId() + "'", e);
307: }
308: }
309:
310: // process instances ////////////////////////////////////////////////////////
311:
312: /**
313: * @deprecated use {@link org.jbpm.JbpmContext#save(ProcessInstance)} instead.
314: * @throws UnsupportedOperationException
315: */
316: public void saveProcessInstance(ProcessInstance processInstance) {
317: throw new UnsupportedOperationException(
318: "use JbpmContext.save(ProcessInstance) instead");
319: }
320:
321: /**
322: * loads a process instance from the database by the identifier.
323: * This throws an exception in case the process instance doesn't exist.
324: * @see #getProcessInstance(long)
325: * @throws JbpmException in case the process instance doesn't exist.
326: */
327: public ProcessInstance loadProcessInstance(long processInstanceId) {
328: try {
329: ProcessInstance processInstance = (ProcessInstance) session
330: .load(ProcessInstance.class, new Long(
331: processInstanceId));
332: return processInstance;
333: } catch (Exception e) {
334: e.printStackTrace();
335: log.error(e);
336: jbpmSession.handleException();
337: throw new JbpmException("couldn't load process instance '"
338: + processInstanceId + "'", e);
339: }
340: }
341:
342: /**
343: * gets a process instance from the database by the identifier.
344: * This method returns null in case the given process instance doesn't exist.
345: */
346: public ProcessInstance getProcessInstance(long processInstanceId) {
347: try {
348: ProcessInstance processInstance = (ProcessInstance) session
349: .get(ProcessInstance.class, new Long(
350: processInstanceId));
351: return processInstance;
352: } catch (Exception e) {
353: e.printStackTrace();
354: log.error(e);
355: jbpmSession.handleException();
356: throw new JbpmException("couldn't get process instance '"
357: + processInstanceId + "'", e);
358: }
359: }
360:
361: /**
362: * loads a token from the database by the identifier.
363: * @return the token.
364: * @throws JbpmException in case the referenced token doesn't exist.
365: */
366: public Token loadToken(long tokenId) {
367: try {
368: Token token = (Token) session.load(Token.class, new Long(
369: tokenId));
370: return token;
371: } catch (Exception e) {
372: e.printStackTrace();
373: log.error(e);
374: jbpmSession.handleException();
375: throw new JbpmException("couldn't load token '" + tokenId
376: + "'", e);
377: }
378: }
379:
380: /**
381: * gets a token from the database by the identifier.
382: * @return the token or null in case the token doesn't exist.
383: */
384: public Token getToken(long tokenId) {
385: try {
386: Token token = (Token) session.get(Token.class, new Long(
387: tokenId));
388: return token;
389: } catch (Exception e) {
390: e.printStackTrace();
391: log.error(e);
392: jbpmSession.handleException();
393: throw new JbpmException("couldn't get token '" + tokenId
394: + "'", e);
395: }
396: }
397:
398: /**
399: * locks a process instance in the database.
400: */
401: public void lockProcessInstance(long processInstanceId) {
402: lockProcessInstance(loadProcessInstance(processInstanceId));
403: }
404:
405: /**
406: * locks a process instance in the database.
407: */
408: public void lockProcessInstance(ProcessInstance processInstance) {
409: try {
410: session.lock(processInstance, LockMode.UPGRADE);
411: } catch (Exception e) {
412: e.printStackTrace();
413: log.error(e);
414: jbpmSession.handleException();
415: throw new JbpmException("couldn't lock process instance '"
416: + processInstance.getId() + "'", e);
417: }
418: }
419:
420: /**
421: * fetches all processInstances for the given process definition from the database.
422: * The returned list of process instances is sorted start date, youngest first.
423: */
424: public List findProcessInstances(long processDefinitionId) {
425: List processInstances = null;
426: try {
427: Query query = session
428: .getNamedQuery("GraphSession.findAllProcessInstancesForADefinition");
429: query.setLong("processDefinitionId", processDefinitionId);
430: processInstances = query.list();
431:
432: } catch (Exception e) {
433: e.printStackTrace();
434: log.error(e);
435: jbpmSession.handleException();
436: throw new JbpmException(
437: "couldn't load process instances for process definition '"
438: + processDefinitionId + "'", e);
439: }
440: return processInstances;
441: }
442:
443: public void deleteProcessInstance(long processInstanceId) {
444: deleteProcessInstance(loadProcessInstance(processInstanceId));
445: }
446:
447: public void deleteProcessInstance(ProcessInstance processInstance) {
448: deleteProcessInstance(processInstance, true, true);
449: }
450:
451: public void deleteProcessInstance(ProcessInstance processInstance,
452: boolean includeTasks, boolean includeJobs) {
453: if (processInstance == null)
454: throw new JbpmException(
455: "processInstance is null in JbpmSession.deleteProcessInstance()");
456: try {
457: // find the tokens
458: Query query = session
459: .getNamedQuery("GraphSession.findTokensForProcessInstance");
460: query.setEntity("processInstance", processInstance);
461: List tokens = query.list();
462:
463: // deleteSubProcesses
464: Iterator iter = tokens.iterator();
465: while (iter.hasNext()) {
466: Token token = (Token) iter.next();
467: deleteSubProcesses(token);
468: }
469:
470: // jobs
471: if (includeJobs) {
472: query = session
473: .getNamedQuery("GraphSession.deleteJobsForProcessInstance");
474: query.setEntity("processInstance", processInstance);
475: query.executeUpdate();
476: }
477:
478: // tasks
479: if (includeTasks) {
480: query = session
481: .getNamedQuery("GraphSession.findTaskInstanceIdsForProcessInstance");
482: query.setEntity("processInstance", processInstance);
483: List taskInstanceIds = query.list();
484:
485: query = session
486: .getNamedQuery("GraphSession.deleteTaskInstancesById");
487: query.setParameterList("taskInstanceIds",
488: taskInstanceIds);
489: }
490:
491: // delete the logs for all the process instance's tokens
492: query = session
493: .getNamedQuery("GraphSession.selectLogsForTokens");
494: query.setParameterList("tokens", tokens);
495: List logs = query.list();
496: iter = logs.iterator();
497: while (iter.hasNext()) {
498: session.delete(iter.next());
499: }
500:
501: // then delete the process instance
502: session.delete(processInstance);
503:
504: } catch (Exception e) {
505: e.printStackTrace();
506: log.error(e);
507: jbpmSession.handleException();
508: throw new JbpmException(
509: "couldn't delete process instance '"
510: + processInstance.getId() + "'", e);
511: }
512: }
513:
514: void deleteSubProcesses(Token token) {
515: Query query = session
516: .getNamedQuery("GraphSession.findSubProcessInstances");
517: query.setEntity("processInstance", token.getProcessInstance());
518: List processInstances = query.list();
519:
520: if (processInstances == null || processInstances.isEmpty()) {
521: return;
522: }
523:
524: Iterator iter = processInstances.iterator();
525: while (iter.hasNext()) {
526: ProcessInstance subProcessInstance = (ProcessInstance) iter
527: .next();
528: subProcessInstance.setSuperProcessToken(null);
529: token.setSubProcessInstance(null);
530: deleteProcessInstance(subProcessInstance);
531: }
532:
533: if (token.getChildren() != null) {
534: iter = token.getChildren().values().iterator();
535: while (iter.hasNext()) {
536: Token child = (Token) iter.next();
537: deleteSubProcesses(child);
538: }
539: }
540: }
541:
542: public static class AverageNodeTimeEntry {
543: private long nodeId;
544: private String nodeName;
545: private int count;
546: private long averageDuration;
547: private long minDuration;
548: private long maxDuration;
549:
550: public long getNodeId() {
551: return nodeId;
552: }
553:
554: public void setNodeId(final long nodeId) {
555: this .nodeId = nodeId;
556: }
557:
558: public String getNodeName() {
559: return nodeName;
560: }
561:
562: public void setNodeName(final String nodeName) {
563: this .nodeName = nodeName;
564: }
565:
566: public int getCount() {
567: return count;
568: }
569:
570: public void setCount(final int count) {
571: this .count = count;
572: }
573:
574: public long getAverageDuration() {
575: return averageDuration;
576: }
577:
578: public void setAverageDuration(final long averageDuration) {
579: this .averageDuration = averageDuration;
580: }
581:
582: public long getMinDuration() {
583: return minDuration;
584: }
585:
586: public void setMinDuration(final long minDuration) {
587: this .minDuration = minDuration;
588: }
589:
590: public long getMaxDuration() {
591: return maxDuration;
592: }
593:
594: public void setMaxDuration(final long maxDuration) {
595: this .maxDuration = maxDuration;
596: }
597: }
598:
599: public List calculateAverageTimeByNode(
600: final long processDefinitionId,
601: final long minumumDurationMillis) {
602: List results = null;
603: try {
604: Query query = session
605: .getNamedQuery("GraphSession.calculateAverageTimeByNode");
606: query.setLong("processDefinitionId", processDefinitionId);
607: query.setDouble("minimumDuration", minumumDurationMillis);
608: List listResults = query.list();
609:
610: if (listResults != null) {
611: results = new ArrayList();
612: Iterator iter = listResults.iterator();
613: while (iter.hasNext()) {
614: Object[] values = (Object[]) iter.next();
615:
616: AverageNodeTimeEntry averageNodeTimeEntry = new AverageNodeTimeEntry();
617: averageNodeTimeEntry.setNodeId(((Number) values[0])
618: .longValue());
619: averageNodeTimeEntry
620: .setNodeName((String) values[1]);
621: averageNodeTimeEntry.setCount(((Number) values[2])
622: .intValue());
623: averageNodeTimeEntry
624: .setAverageDuration(((Number) values[3])
625: .longValue());
626: averageNodeTimeEntry
627: .setMinDuration(((Number) values[4])
628: .longValue());
629: averageNodeTimeEntry
630: .setMaxDuration(((Number) values[5])
631: .longValue());
632:
633: results.add(averageNodeTimeEntry);
634: }
635: }
636: } catch (Exception e) {
637: e.printStackTrace();
638: log.error(e);
639: jbpmSession.handleException();
640: throw new JbpmException(
641: "couldn't load process instances for process definition '"
642: + processDefinitionId + "'", e);
643: }
644: return results;
645: }
646:
647: public List findActiveNodesByProcessInstance(
648: ProcessInstance processInstance) {
649: List results = null;
650: try {
651: Query query = session
652: .getNamedQuery("GraphSession.findActiveNodesByProcessInstance");
653: query.setEntity("processInstance", processInstance);
654: results = query.list();
655:
656: } catch (Exception e) {
657: e.printStackTrace();
658: log.error(e);
659: jbpmSession.handleException();
660: throw new JbpmException(
661: "couldn't active nodes for process instance '"
662: + processInstance + "'", e);
663: }
664: return results;
665: }
666:
667: public ProcessInstance getProcessInstance(
668: ProcessDefinition processDefinition, String key) {
669: ProcessInstance processInstance = null;
670: try {
671: Query query = session
672: .getNamedQuery("GraphSession.findProcessInstanceByKey");
673: query.setEntity("processDefinition", processDefinition);
674: query.setString("key", key);
675: processInstance = (ProcessInstance) query.uniqueResult();
676:
677: } catch (Exception e) {
678: e.printStackTrace();
679: log.error(e);
680: jbpmSession.handleException();
681: throw new JbpmException(
682: "couldn't get process instance with key '" + key
683: + "'", e);
684: }
685: return processInstance;
686: }
687:
688: public ProcessInstance loadProcessInstance(
689: ProcessDefinition processDefinition, String key) {
690: ProcessInstance processInstance = null;
691: try {
692: Query query = session
693: .getNamedQuery("GraphSession.findProcessInstanceByKey");
694: query.setEntity("processDefinition", processDefinition);
695: query.setString("key", key);
696: processInstance = (ProcessInstance) query.uniqueResult();
697: if (processInstance == null) {
698: throw new JbpmException(
699: "no process instance was found with key " + key);
700: }
701:
702: } catch (Exception e) {
703: e.printStackTrace();
704: log.error(e);
705: jbpmSession.handleException();
706: throw new JbpmException(
707: "couldn't load process instance with key '" + key
708: + "'", e);
709: }
710: return processInstance;
711: }
712:
713: private static final Log log = LogFactory
714: .getLog(GraphSession.class);
715: }
|