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;
023:
024: import java.io.Serializable;
025: import java.sql.Connection;
026: import java.util.ArrayList;
027: import java.util.Iterator;
028: import java.util.List;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032: import org.hibernate.Session;
033: import org.hibernate.SessionFactory;
034: import org.jbpm.configuration.ObjectFactory;
035: import org.jbpm.db.ContextSession;
036: import org.jbpm.db.GraphSession;
037: import org.jbpm.db.JobSession;
038: import org.jbpm.db.LoggingSession;
039: import org.jbpm.db.TaskMgmtSession;
040: import org.jbpm.graph.def.ProcessDefinition;
041: import org.jbpm.graph.exe.ProcessInstance;
042: import org.jbpm.graph.exe.Token;
043: import org.jbpm.persistence.PersistenceService;
044: import org.jbpm.persistence.db.DbPersistenceService;
045: import org.jbpm.security.authentication.DefaultAuthenticationService;
046: import org.jbpm.svc.ServiceFactory;
047: import org.jbpm.svc.Services;
048: import org.jbpm.taskmgmt.exe.TaskInstance;
049: import org.jbpm.tx.TxService;
050:
051: /**
052: * is used to surround persistent operations to processes.
053: *
054: * <p>Obtain JbpmContext's via {@link org.jbpm.JbpmConfiguration#createJbpmContext()}
055: * and put it in a try-finally block like this:
056: * </p>
057: *
058: * <pre>
059: * JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
060: * try {
061: * TaskInstance taskInstance = ...
062: *
063: * ...do your process operations...
064: *
065: * // in case you update a process object that was not fetched
066: * // with a ...ForUpdate method, you have to save it.
067: * jbpmContext.save(processInstance);
068: * finally {
069: * jbpmContext.close();
070: * }
071: * </pre>
072: *
073: * <p>A JbpmContext separates jBPM from a sprecific environment.
074: * For each service that jBPM uses, there is an interface specified in the jBPM codebase.
075: * jBPM also includes implementations that implement these services by using services in a
076: * specific environment. e.g. a hibernate session, a JMS asynchronous messaging system, ...
077: * </p>
078: *
079: * <p>A JbpmContext can demarcate a transaction. When a PersistenceService is fetched from
080: * the JbpmContext, the default implementation for the persistence service will create
081: * a hibernate session and start a transaction. So that transactions can be configured
082: * in the hibernate configuration.
083: * </p>
084: *
085: * <p>A JbpmContext allows the user to overwrite (or make complete) the configuration
086: * by injecting objects programmatically. like e.g. a hibernate session factory or
087: * a hibernate session or any other resource that can be fetched or created from the
088: * configuration.
089: * </p>
090: *
091: * <p>Last but not least, JbpmContext provides convenient access to the most common
092: * operations such as {@link #getTaskList(String)}, {@link #newProcessInstance(String)}
093: * {@link #loadTaskInstanceForUpdate(long)} and {@link #save(ProcessInstance)}.
094: * </p>
095: *
096: * <p>All the <code>...ForUpdate(...)</code> methods will automatically save the loaded
097: * objects at <code>jbpmContext.close();</code>
098: * </p>
099: */
100: public class JbpmContext implements Serializable {
101:
102: private static final long serialVersionUID = 1L;
103:
104: public static final String DEFAULT_JBPM_CONTEXT_NAME = "default.jbpm.context";
105:
106: static ThreadLocal currentContextsStack = new ThreadLocal();
107:
108: /**
109: * resets static members for test isolation.
110: */
111: static void reset() {
112: currentContextsStack = new ThreadLocal();
113: }
114:
115: ObjectFactory objectFactory = null;
116: Services services = null;
117: List autoSaveProcessInstances = null;
118: JbpmConfiguration jbpmConfiguration = null;
119:
120: /**
121: * normally, JbpmContext object are created via a {@link JbpmConfiguration}.
122: */
123: public JbpmContext(Services services, ObjectFactory objectFactory) {
124: log.debug("creating " + toString());
125: this .services = services;
126: this .objectFactory = objectFactory;
127: }
128:
129: /**
130: * make sure you close your JbpmContext in a finally block.
131: */
132: public void close() {
133: log.debug("closing jbpmContext " + toString());
134: try {
135: if (services != null) {
136: try {
137: autoSave();
138: } finally {
139: services.close();
140: }
141: }
142: } finally {
143: if (jbpmConfiguration != null) {
144: jbpmConfiguration.jbpmContextClosed(this );
145: }
146: }
147: }
148:
149: /**
150: * obtains the current JbpmContext from a thread local.
151: * The current JbpmContexts are maintained in a stack so
152: * that you can do nested context operations for
153: * different jbpm configurations.
154: * @deprecated method moved to {@link JbpmConfiguration}.
155: */
156: public static JbpmContext getCurrentJbpmContext() {
157: JbpmContext currentJbpmContext = null;
158: JbpmConfiguration currentJbpmConfiguration = JbpmConfiguration
159: .getCurrentJbpmConfiguration();
160: if (currentJbpmConfiguration != null) {
161: currentJbpmContext = currentJbpmConfiguration
162: .getCurrentJbpmContext();
163: }
164: return currentJbpmContext;
165: }
166:
167: // convenience methods //////////////////////////////////////////////////////
168:
169: /**
170: * deploys a process definition.
171: * For parsing process definitions from archives, see the static parseXxx methods
172: * on {@link ProcessDefinition}.
173: */
174: public void deployProcessDefinition(
175: ProcessDefinition processDefinition) {
176: getGraphSession().deployProcessDefinition(processDefinition);
177: }
178:
179: /**
180: * fetches the tasklist for the current authenticated actor. With the default
181: * configured authentication service, you can set the authenticated user
182: * with {@link #setActorId(String)}, then all the subsequent operations will
183: * be performed on behalf of that actor.
184: */
185: public List getTaskList() {
186: String actorId = getActorId();
187: return getTaskMgmtSession().findTaskInstances(actorId);
188: }
189:
190: /**
191: * fetches the tasklist for the given actor.
192: */
193: public List getTaskList(String actorId) {
194: return getTaskMgmtSession().findTaskInstances(actorId);
195: }
196:
197: /**
198: * fetches all the task instances for which at least one of the given
199: * actorIds is a candidate (pooled actor).
200: * Typically, for an actor, his/her personal actorId plus
201: * all the actorIds representing the groups that person belongs
202: * to form the actorIds. Then the user interface should show
203: * only the option to take these tasks to the actor's personal
204: * task list (with {@link TaskInstance#setActorId(String)}). Only
205: * task instances that are assigned to the actor directly should be
206: * offered the possibility for performing the actual task.
207: */
208: public List getGroupTaskList(List actorIds) {
209: return getTaskMgmtSession().findPooledTaskInstances(actorIds);
210: }
211:
212: /**
213: * loads a task instance from the db.
214: * @throws JbpmException in case no such task instance exists
215: * @see #getTaskInstance(long)
216: * @see #loadTaskInstanceForUpdate(long)
217: * @see #getTaskInstanceForUpdate(long)
218: */
219: public TaskInstance loadTaskInstance(long taskInstanceId) {
220: return getTaskMgmtSession().loadTaskInstance(taskInstanceId);
221: }
222:
223: /**
224: * gets a task instance from the db.
225: * @return the task instance or null in case no such task instance exists.
226: * @see #loadTaskInstance(long)
227: * @see #loadTaskInstanceForUpdate(long)
228: * @see #getTaskInstanceForUpdate(long)
229: */
230: public TaskInstance getTaskInstance(long taskInstanceId) {
231: return getTaskMgmtSession().getTaskInstance(taskInstanceId);
232: }
233:
234: /**
235: * loads a task instance from the db and registers it for auto-save.
236: * The loaded task instance will be save automatically at the {@link #close()}.
237: * This is a convenience method in case you plan to do update operations on
238: * this task instance.
239: * @throws JbpmException in case no such task instance exists
240: * @see #loadTaskInstance(long)
241: * @see #getTaskInstance(long)
242: * @see #getTaskInstanceForUpdate(long)
243: */
244: public TaskInstance loadTaskInstanceForUpdate(long taskInstanceId) {
245: TaskInstance taskInstance = getTaskMgmtSession()
246: .loadTaskInstance(taskInstanceId);
247: addAutoSaveTaskInstance(taskInstance);
248: return taskInstance;
249: }
250:
251: /**
252: * gets a task instance from the db and registers it for auto-save.
253: * The loaded task instance will be save automatically at the {@link #close()}.
254: * This is a convenience method in case you plan to do update operations on
255: * this task instance.
256: * @return the task instance or null in case no such task instance exists.
257: * @see #loadTaskInstance(long)
258: * @see #getTaskInstance(long)
259: * @see #loadTaskInstanceForUpdate(long)
260: */
261: public TaskInstance getTaskInstanceForUpdate(long taskInstanceId) {
262: TaskInstance taskInstance = getTaskMgmtSession()
263: .getTaskInstance(taskInstanceId);
264: if (taskInstance != null) {
265: addAutoSaveTaskInstance(taskInstance);
266: }
267: return taskInstance;
268: }
269:
270: /**
271: * loads a token from the db.
272: * @throws JbpmException in case no such token exists.
273: * @see #getToken(long)
274: * @see #loadTokenForUpdate(long)
275: * @see #getTokenForUpdate(long)
276: */
277: public Token loadToken(long tokenId) {
278: return getGraphSession().loadToken(tokenId);
279: }
280:
281: /**
282: * gets a token from the db.
283: * @return the token or null in case no such token exists.
284: * @see #loadToken(long)
285: * @see #loadTokenForUpdate(long)
286: * @see #getTokenForUpdate(long)
287: */
288: public Token getToken(long tokenId) {
289: return getGraphSession().getToken(tokenId);
290: }
291:
292: /**
293: * loads a token from the db and registers it for auto-save.
294: * The loaded token will be {@link #save(Token)}d automatically at the {@link #close()}.
295: * This is a convenience method in case you plan to do update operations on
296: * this token.
297: * @throws JbpmException in case no such token exists.
298: * @see #getToken(long)
299: * @see #loadToken(long)
300: * @see #getTokenForUpdate(long)
301: */
302: public Token loadTokenForUpdate(long tokenId) {
303: Token token = getGraphSession().loadToken(tokenId);
304: addAutoSaveToken(token);
305: return token;
306: }
307:
308: /**
309: * get a token from the db and registers it for auto-save.
310: * The loaded token will be {@link #save(Token)}d automatically at the {@link #close()}.
311: * This is a convenience method in case you plan to do update operations on
312: * this token.
313: * @return the token or null in case no such token exists.
314: * @see #getToken(long)
315: * @see #loadToken(long)
316: * @see #loadTokenForUpdate(long)
317: */
318: public Token getTokenForUpdate(long tokenId) {
319: Token token = getGraphSession().getToken(tokenId);
320: if (token != null) {
321: addAutoSaveToken(token);
322: }
323: return token;
324: }
325:
326: /**
327: * loads a process instance from the db.
328: * Consider using {@link #loadProcessInstanceForUpdate(long)} if you plan to
329: * perform an update operation on the process instance.
330: * @throws JbpmException in case no such process instance exists.
331: * @see #getProcessInstance(long)
332: * @see #loadProcessInstanceForUpdate(long)
333: * @see #getProcessInstanceForUpdate(long)
334: */
335: public ProcessInstance loadProcessInstance(long processInstanceId) {
336: return getGraphSession().loadProcessInstance(processInstanceId);
337: }
338:
339: /**
340: * gets a process instance from the db.
341: * Consider using {@link #loadProcessInstanceForUpdate(long)} if you plan to
342: * perform an update operation on the process instance.
343: * @return the token or null in case no such token exists.
344: * @see #loadProcessInstance(long)
345: * @see #loadProcessInstanceForUpdate(long)
346: * @see #getProcessInstanceForUpdate(long)
347: */
348: public ProcessInstance getProcessInstance(long processInstanceId) {
349: return getGraphSession().getProcessInstance(processInstanceId);
350: }
351:
352: /**
353: * loads a process instances from the db and registers it for auto-save.
354: * The loaded process instance will be {@link #save(ProcessInstance)}d automatically
355: * at the {@link #close()}. This is a convenience method in case you plan to do update
356: * operations on this process instance.
357: * @throws JbpmException in case no such process instance exists.
358: * @see #loadProcessInstance(long)
359: * @see #getProcessInstance(long)
360: * @see #getProcessInstanceForUpdate(long)
361: */
362: public ProcessInstance loadProcessInstanceForUpdate(
363: long processInstanceId) {
364: ProcessInstance processInstance = getGraphSession()
365: .loadProcessInstance(processInstanceId);
366: addAutoSaveProcessInstance(processInstance);
367: return processInstance;
368: }
369:
370: /**
371: * gets a process instances from the db and registers it for auto-save.
372: * The loaded process instance will be {@link #save(ProcessInstance)}d automatically
373: * at the {@link #close()}. This is a convenience method in case you plan to do update
374: * operations on this process instance.
375: * @return the token or null in case no such token exists.
376: * @see #loadProcessInstance(long)
377: * @see #getProcessInstance(long)
378: * @see #loadProcessInstanceForUpdate(long)
379: */
380: public ProcessInstance getProcessInstanceForUpdate(
381: long processInstanceId) {
382: ProcessInstance processInstance = getGraphSession()
383: .getProcessInstance(processInstanceId);
384: if (processInstance != null) {
385: addAutoSaveProcessInstance(processInstance);
386: }
387: return processInstance;
388: }
389:
390: /** returns the process instance with the given key or null if no such instance exists.
391: */
392: public ProcessInstance getProcessInstance(
393: ProcessDefinition processDefinition, String key) {
394: return getGraphSession().getProcessInstance(processDefinition,
395: key);
396: }
397:
398: /** returns the process instance with the given key or throws an exception if no
399: * such instance exists.
400: */
401: public ProcessInstance loadProcessInstance(
402: ProcessDefinition processDefinition, String key) {
403: return getGraphSession().loadProcessInstance(processDefinition,
404: key);
405: }
406:
407: /** returns the process instance with the given key or null if no such instance exists.
408: * Upon close of this jbpmContext, the fetched process instance will be automatically saved.
409: */
410: public ProcessInstance getProcessInstanceForUpdate(
411: ProcessDefinition processDefinition, String key) {
412: ProcessInstance processInstance = getGraphSession()
413: .getProcessInstance(processDefinition, key);
414: if (processInstance != null) {
415: addAutoSaveProcessInstance(processInstance);
416: }
417: return processInstance;
418: }
419:
420: /** returns the process instance with the given key or throws an exception if no
421: * such instance exists.
422: * Upon close of this jbpmContext, the fetched process instance will be automatically saved.
423: */
424: public ProcessInstance loadProcessInstanceForUpdate(
425: ProcessDefinition processDefinition, String key) {
426: ProcessInstance processInstance = getGraphSession()
427: .loadProcessInstance(processDefinition, key);
428: if (processInstance != null) {
429: addAutoSaveProcessInstance(processInstance);
430: }
431: return processInstance;
432: }
433:
434: /**
435: * creates a new process instance for the latest version of the process definition
436: * with the given name.
437: * @throws JbpmException when no processDefinition with the given name is deployed.
438: */
439: public ProcessInstance newProcessInstance(
440: String processDefinitionName) {
441: ProcessDefinition processDefinition = getGraphSession()
442: .findLatestProcessDefinition(processDefinitionName);
443: return new ProcessInstance(processDefinition);
444: }
445:
446: /**
447: * creates a new process instance for the latest version of the process definition
448: * with the given name and registers it for auto-save.
449: * @throws JbpmException when no processDefinition with the given name is deployed.
450: */
451: public ProcessInstance newProcessInstanceForUpdate(
452: String processDefinitionName) {
453: ProcessDefinition processDefinition = getGraphSession()
454: .findLatestProcessDefinition(processDefinitionName);
455: ProcessInstance processInstance = new ProcessInstance(
456: processDefinition);
457: addAutoSaveProcessInstance(processInstance);
458: return processInstance;
459: }
460:
461: /**
462: * saves the process instance.
463: */
464: public void save(ProcessInstance processInstance) {
465: if (services != null) {
466: services.save(processInstance, this );
467: }
468: }
469:
470: /**
471: * saves the complete process instance for this token.
472: */
473: public void save(Token token) {
474: save(token.getProcessInstance());
475: }
476:
477: /**
478: * saves the complete process instance for this task instance.
479: */
480: public void save(TaskInstance taskInstance) {
481: save(taskInstance.getTaskMgmtInstance().getProcessInstance());
482: }
483:
484: /**
485: * mark this transaction for rollback only in the persistence service.
486: * The {@link #close()} operation will then perform a rollback.
487: */
488: public void setRollbackOnly() {
489: TxService txService = (services != null ? services
490: .getTxService() : null);
491: if (txService != null) {
492: txService.setRollbackOnly();
493: } else {
494: throw new JbpmException("no transaction service configured");
495: }
496: }
497:
498: // services //////////////////////////////////////////////////////////
499:
500: /**
501: * gives access to the services and service factories.
502: */
503: public Services getServices() {
504: return services;
505: }
506:
507: public ServiceFactory getServiceFactory(String name) {
508: return services.getServiceFactory(name);
509: }
510:
511: /**
512: * gives access to the object factory containing the configuration
513: * for creating the service factories.
514: */
515: public ObjectFactory getObjectFactory() {
516: return objectFactory;
517: }
518:
519: // persistence methods //////////////////////////////////////////////////////
520:
521: /**
522: * gets the hibernate session factory from the default configured persistence
523: * service.
524: * @throws ClassCastException if another persistence service is configured then the default.
525: */
526: public SessionFactory getSessionFactory() {
527: DbPersistenceService persistenceService = (DbPersistenceService) getPersistenceService();
528: if (persistenceService == null)
529: return null;
530: return persistenceService.getSessionFactory();
531: }
532:
533: /**
534: * sets the hibernate session factory into the default configured persistence
535: * service, overwriting the configured session factory (if there is one configured).
536: * @throws ClassCastException if another persistence service is configured then the default.
537: */
538: public void setSessionFactory(SessionFactory sessionFactory) {
539: PersistenceService persistenceService = getPersistenceService();
540: if (persistenceService == null)
541: return;
542: persistenceService.setSessionFactory(sessionFactory);
543: }
544:
545: /**
546: * gets the hibernate session from the default configured persistence
547: * service.
548: * @throws ClassCastException if another persistence service is configured then the default.
549: */
550: public Session getSession() {
551: DbPersistenceService persistenceService = (DbPersistenceService) getPersistenceService();
552: if (persistenceService == null)
553: return null;
554: return persistenceService.getSession();
555: }
556:
557: /**
558: * sets the hibernate session into the default configured persistence
559: * service, preventing the creation of a session from the configured
560: * session factory (if there is one configured).
561: * @throws ClassCastException if another persistence service is configured then the default.
562: */
563: public void setSession(Session session) {
564: DbPersistenceService persistenceService = (DbPersistenceService) getPersistenceService();
565: if (persistenceService == null)
566: return;
567: persistenceService.setSession(session);
568: }
569:
570: /**
571: * gets the jdbc connection from the default configured persistence service.
572: * @throws ClassCastException if another persistence service is configured then the default.
573: */
574: public Connection getConnection() {
575: DbPersistenceService persistenceService = (DbPersistenceService) getPersistenceService();
576: if (persistenceService == null)
577: return null;
578: return persistenceService.getConnection();
579: }
580:
581: /**
582: * allows users to provide a jdbc connection to be used when the hibernate
583: * session is created.
584: * @throws ClassCastException if another persistence service is configured then the default.
585: */
586: public void setConnection(Connection connection) {
587: DbPersistenceService persistenceService = (DbPersistenceService) getPersistenceService();
588: if (persistenceService == null)
589: return;
590: persistenceService.setConnection(connection);
591: }
592:
593: // jbpm database access sessions
594:
595: /**
596: * more variables related database access.
597: */
598: public ContextSession getContextSession() {
599: PersistenceService persistenceService = getPersistenceService();
600: if (persistenceService == null)
601: return null;
602: return persistenceService.getContextSession();
603: }
604:
605: /**
606: * more logging related database access.
607: */
608: public LoggingSession getLoggingSession() {
609: PersistenceService persistenceService = getPersistenceService();
610: if (persistenceService == null)
611: return null;
612: return (persistenceService != null ? persistenceService
613: .getLoggingSession() : null);
614: }
615:
616: /**
617: * more job related database access.
618: */
619: public JobSession getJobSession() {
620: PersistenceService persistenceService = getPersistenceService();
621: if (persistenceService == null)
622: return null;
623: return (persistenceService != null ? persistenceService
624: .getJobSession() : null);
625: }
626:
627: /**
628: * more graph (process) related database access.
629: */
630: public GraphSession getGraphSession() {
631: PersistenceService persistenceService = getPersistenceService();
632: if (persistenceService == null)
633: return null;
634: return (persistenceService != null ? persistenceService
635: .getGraphSession() : null);
636: }
637:
638: /**
639: * more task related database access.
640: */
641: public TaskMgmtSession getTaskMgmtSession() {
642: PersistenceService persistenceService = getPersistenceService();
643: if (persistenceService == null)
644: return null;
645: return (persistenceService != null ? persistenceService
646: .getTaskMgmtSession() : null);
647: }
648:
649: // authentication methods ///////////////////////////////////////////////////
650:
651: /**
652: * retrieves the current authenticated actor from the authentication service.
653: */
654: public String getActorId() {
655: return services.getAuthenticationService().getActorId();
656: }
657:
658: /**
659: * sets the currently authenticated actorId.
660: * @throws ClassCastException if another authentication service is configured then the default.
661: */
662: public void setActorId(String actorId) {
663: DefaultAuthenticationService authenticationService = (DefaultAuthenticationService) services
664: .getAuthenticationService();
665: DefaultAuthenticationService defaultAuthenticationService = (DefaultAuthenticationService) authenticationService;
666: defaultAuthenticationService.setActorId(actorId);
667: }
668:
669: // private methods //////////////////////////////////////////////////////////
670:
671: void addAutoSaveProcessInstance(ProcessInstance processInstance) {
672: if (autoSaveProcessInstances == null)
673: autoSaveProcessInstances = new ArrayList();
674: autoSaveProcessInstances.add(processInstance);
675: }
676:
677: void addAutoSaveToken(Token token) {
678: addAutoSaveProcessInstance(token.getProcessInstance());
679: }
680:
681: void addAutoSaveTaskInstance(TaskInstance taskInstance) {
682: addAutoSaveProcessInstance(taskInstance.getTaskMgmtInstance()
683: .getProcessInstance());
684: }
685:
686: void autoSave() {
687: if (autoSaveProcessInstances != null) {
688: Iterator iter = autoSaveProcessInstances.iterator();
689: while (iter.hasNext()) {
690: ProcessInstance processInstance = (ProcessInstance) iter
691: .next();
692: save(processInstance);
693: iter.remove();
694: }
695: }
696: }
697:
698: PersistenceService getPersistenceService() {
699: if (services == null)
700: return null;
701: return services.getPersistenceService();
702: }
703:
704: public JbpmConfiguration getJbpmConfiguration() {
705: return jbpmConfiguration;
706: }
707:
708: private static Log log = LogFactory.getLog(JbpmContext.class);
709: }
|