View Javadoc

1   /*
2    * Copyright 2010 Fraunhofer Gesellschaft, Munich, Germany,
3    * for its Fraunhofer Institute for Computer Architecture and Software
4    * Technology (FIRST), Berlin, Germany. All rights reserved.
5    * http://www.first.fraunhofer.de/
6    */
7   
8   package net.kwfgrid.gwes;
9   
10  import net.kwfgrid.gwes.exception.ActivityException;
11  import net.kwfgrid.gwes.exception.LoggingException;
12  import net.kwfgrid.gwes.util.HTMLFilter;
13  import net.kwfgrid.gworkflowdl.structure.*;
14  import org.apache.log4j.Logger;
15  
16  /**
17   * This abstract class is to be extended in order to implement a concrete activity, such as a remote procedure call.
18   * Activities are single process steps of a workflow. In order to implement your own activity class, you should
19   * override the methods
20   * <PRE>
21   * initiateActivity()
22   * startActivity()
23   * simulateActivity()
24   * suspendActivity()
25   * resumeActivity()
26   * abortActivity()
27   * restartActivity()
28   * cleanupActivity()
29   * </PRE>
30   *
31   * @author Andreas Hoheisel
32   *         (<a href="http://www.andreas-hoheisel.de">www.andreas-hoheisel.de</a>)
33   * @version $Id: Activity.java 1534 2011-06-29 14:53:44Z hoheisel $
34   * @see #initiateActivity()
35   * @see #startActivity()
36   * @see #simulateActivity()
37   * @see #suspendActivity()
38   * @see #resumeActivity()
39   * @see #abortActivity()
40   * @see #restartActivity()
41   * @see #cleanupActivity()
42   */
43  public abstract class Activity {
44  
45      /**
46       * log4j logger
47       */
48      public static final Logger logger = Logger.getLogger(Activity.class);
49  
50      /**
51       * Maximum wait in MS
52       */
53      private static final long WAIT = Long.parseLong(System.getProperty(Constants.PROP_SYSTEM_WORKFLOW_SLEEPTIME_MAX,"60000"));
54  
55      /**
56       * Identifier of this Activity
57       */
58      public final String ID;
59  
60      /**
61       * The parent workflow handler that owns this activity
62       */
63      public GenericWorkflowHandler handler;
64  
65      /**
66       * The status of this activity. Use <CODE>setStatus()</CODE> in order to modify the status.
67       *
68       * @see #setStatus(Status)
69       */
70      private Status status;
71  
72      /**
73       * The transition occurrence contains all the workflow information needed for this activity.
74       */
75      protected TransitionOccurrence to;
76  
77      /**
78       * The operation candidate contains all information about the operation and its resources.
79       */
80      protected OperationCandidate op;
81  
82      /**
83       * Timeout for this activity in state running or active in milliseconds.
84       */
85      public long timeoutRunning;
86  
87      /**
88       * Timeout for this activity in state active in milliseconds.
89       */
90      public long timeoutActive;
91  
92      /**
93       * Fault message.
94       * Is used to store human-readable error messages.
95       */
96      private String faultMessage;
97  
98      /**
99       * Fault as SOAPFault object.
100      */
101     private SOAPFault soapFault;
102 
103     /**
104      * Logging facility.
105      */
106     private GWESLogger glog;
107 
108     /**
109      * Pending number of attempts of this activity. If attempts=1, then it is the last attempt.
110      * A value of -1 means that attempts is not set.
111      */
112     private int attempts = -1;
113 
114     /**
115      * time in milliseconds status being undefined and not yet initialized.
116      */
117     private long durationUndefined = 0L;
118     private long startTimeUndefined = 0L;
119 
120     /**
121      * time in milliseconds status being initiated and not running.
122      */
123     private long durationInitiated = 0L;
124     private long startTimeInitiated = 0L;
125 
126     /**
127      * time in milliseconds status being running (not including active).
128      */
129     private long durationRunning = 0L;
130     private long startTimeRunning = 0L;
131 
132     /**
133      * time in milliseconds status being active.
134      */
135     private long durationActive = 0L;
136     private long startTimeActive = 0L;
137 
138     /**
139      * time in milliseconds status being suspended.
140      */
141     private long durationSuspended = 0L;
142     private long startTimeSuspended = 0L;
143 
144     /**
145      * total activity duration in milliseconds.
146      */
147     private long durationTotal = 0L;
148     private long startTimeTotal = 0L;
149 
150     /**
151      * Default timeoutRunning for activities in state "RUNNING" or "ACTIVE" in milliseconds.
152      * default = 1000*60*60*24 = 86400000 => 1 Day
153      */
154     public static final long DEFAULT_TIMEOUT_RUNNING = 86400000;
155 
156     /**
157      * Default timeoutActive for activities in state "ACTIVE" in milliseconds.
158      * default = 1000*60*60*2 = 7200000 => 2 Hours
159      */
160     public static final long DEFAULT_TIMEOUT_ACTIVE = 7200000;
161 
162     /**
163      * Valid status for activities:
164      * <br>Status <B>UNDEFINED</B>. The status remains <CODE>UNDEFINED</CODE> from the construction of the activity
165      * until the method <CODE>initiateActivity()</CODE> has been called.
166      * <br>Status <B>INITIATED</B>. The activity has been created and initialized but not yet been started or aborted.
167      * <br>Status <B>RUNNING</B>. The activity has been started but it is not yet active or complete and it has not
168      * been suspended or aborted.
169      * <br>Status <B>SUSPENDED</B>. The activity is quiescent and can be returned
170      * to the running state via <CODE>resumeActivity()</CODE>.
171      * <br>Status <B>ACTIVE</B>. Resources related to this activity have been allocated and/or sub activities have
172      * been started.
173      * <br>Status <B>TERMINATED</B>. The execution of this activity instance has aborted before its normal completion.
174      * This can be due to an activity exception (activity failed) or via the method <CODE>abortActivity()</CODE>.
175      * <br>Status <B>COMPLETED</B>. The activity instance has fulfilled the conditions for completion. The activity
176      * instance will be destroyed.
177      * <br>Status <B>FAILED</B>. Activity has failed. It will be retried.
178      */
179     public static enum Status {
180         UNDEFINED,
181         INITIATED,
182         RUNNING,
183         SUSPENDED,
184         ACTIVE,
185         TERMINATED,
186         COMPLETED,
187         FAILED
188     }
189 
190     /**
191      * saves the last exitCode in call stack for SOAPFault
192      */
193     public Integer lastExitCode;
194 
195     /**
196      * Constructor for Activity.
197      * In order to access the input and output objects use the TransitionOccurrence class.
198      *
199      * @param handler The parent generic workflow handler.
200      * @param to The TransitionOccurrence object with workflow data about this activity invocation.
201      * @param op The OperationCandidate with information about the operation and its resources.
202      */
203     public Activity(GenericWorkflowHandler handler, TransitionOccurrence to, OperationCandidate op) throws WorkflowFormatException, LoggingException {
204         this.handler = handler;
205         this.ID = (handler == null) ? "Activity_"+ java.util.UUID.randomUUID() : this.handler.getNewActivityID();
206         this.to = to;
207         this.op = op;
208         this.glog = (handler == null) ? GWESLogger.getInstance() : handler.getGlog();
209         setAttempts();
210         setActivityTimeouts();
211         setStatus(Status.UNDEFINED);
212         if (logger.isDebugEnabled()) {
213             logger.debug(this.to);
214         }
215     }
216 
217     /**
218      * Set status of the activity and notify threads that wait for a status change.
219      */
220     public synchronized void setStatus(Status status) {
221         if (status != this.status) {
222             Status oldStatus = this.status;
223             this.status = status;
224             long now = System.currentTimeMillis();
225 
226             try {
227                 glog.logEventSP(
228                         GWESLogger.Event.ACTIVITY_STATUS_CHANGE,
229                         (handler == null) ? null : getHandler().getUserID(),
230                         this,
231                         "activityStatus",
232                         (oldStatus == null) ? null : getStatusAsString(oldStatus),
233                         getStatusAsString(status));
234             } catch (LoggingException e) {
235                 logger.warn("Exception during log: "+e,e);
236             }
237 
238             // statistics
239             switch (this.status) {
240                 case UNDEFINED:
241                     startTimeUndefined = now;
242                     if (startTimeTotal == 0L) {
243                         startTimeTotal = now;
244                     }
245                     break;
246                 case INITIATED:
247                     startTimeInitiated = now;
248                     break;
249                 case RUNNING:
250                     startTimeRunning = now;
251                     break;
252                 case SUSPENDED:
253                     startTimeSuspended = now;
254                     break;
255                 case ACTIVE:
256                     startTimeActive = now;
257                     break;
258                 case COMPLETED:
259                     if (handler != null) handler.incrementCompletedActivities(1);
260                     break;
261                 case TERMINATED:
262                     if (handler != null) handler.incrementTerminatedActivities(1);
263                     break;
264                 case FAILED:
265                     break;
266             }
267 
268             if (oldStatus != null) {
269                 switch (oldStatus) {
270                     case UNDEFINED:
271                         durationUndefined = now - startTimeUndefined;
272                         break;
273                     case INITIATED:
274                         durationInitiated += now - startTimeInitiated;
275                         break;
276                     case RUNNING:
277                         durationRunning += now - startTimeRunning;
278                         break;
279                     case SUSPENDED:
280                         durationSuspended += now - startTimeSuspended;
281                         break;
282                     case ACTIVE:
283                         durationActive += now - startTimeActive;
284                         break;
285                 }
286             }
287 
288             if (isFinalStatus() || status==Status.FAILED) {
289                 durationTotal = now - startTimeTotal;
290                 logStatistics();
291             }
292 
293             // notify waitForStatusChange()
294             synchronized (this) {
295                 this.notifyAll();
296             }
297         }
298     }
299 
300     /**
301      * Get the current status of this activity as int.
302      *
303      * @return current activity status
304      * @see #getStatusAsString()
305      */
306     public Status getStatus() {
307         return status;
308     }
309 
310     /**
311      * Wait for activity to change its status.
312      *
313      * @param oldStatus The old status
314      * @return The new status
315      * @throws InterruptedException If the thread has been interrupted
316      */
317     public Status waitForStatusChangeFrom(Status oldStatus) throws InterruptedException {
318         while (status == oldStatus) {
319             synchronized (this) {
320                 this.wait(WAIT);
321             }
322             getStatus(); // refresh status
323         }
324         return status;
325     }
326 
327     /**
328      * Wait for activity to change its status to a specified status.
329      *
330      * @param newStatus The new status code to wait for
331      * @throws InterruptedException If the thread has been interrupted
332      */
333     public void waitForStatusChangeTo(Status newStatus) throws InterruptedException {
334         while (status != newStatus) {
335             synchronized (this) {
336                 this.wait(WAIT);
337             }
338             getStatus(); // refresh status
339         }
340     }
341 
342     /**
343      * Wait for activity to change its status to COMPLETED or TERMINATED.
344      *
345      * @throws InterruptedException If the thread has been interrupted
346      */
347     public void waitForCompleteOrTerminate() throws InterruptedException {
348         while (!isFinalStatus()) {
349             synchronized (this) {
350                 this.wait(WAIT);
351             }
352             getStatus(); // refresh status
353         }
354     }
355 
356     public boolean isFinalStatus() {
357         return isFinalStatus(status);
358     }
359 
360     public static boolean isFinalStatus(Status status) {
361         switch(status) {
362             case COMPLETED: return true;
363             case TERMINATED: return true;
364         }
365         return false;
366     }
367 
368     /**
369      * Get the current status of the activity as string.
370      *
371      * @return string representing the status of the activity.
372      *         This String is useful for user-readable output.
373      * @see #getStatus()
374      * @see #getStatusAsString(Status)
375      */
376     public String getStatusAsString() {
377         return getStatusAsString(status);
378     }
379 
380     /**
381      * Convert the status of a activity from an integer to a string.
382      *
383      * @param status The status code with should be converted into a string.
384      * @return string representing the status that corresponds to the status code.
385      */
386     public static String getStatusAsString(Status status) {
387         if (status==null) return "null";
388         switch(status) {
389             case UNDEFINED: return "UNDEFINED";
390             case INITIATED: return "INITIATED";
391             case RUNNING: return "RUNNING";
392             case SUSPENDED: return "SUSPENDED";
393             case ACTIVE: return "ACTIVE";
394             case TERMINATED: return "TERMINATED";
395             case COMPLETED: return "COMPLETED";
396             case FAILED: return "FAILED";
397         }
398         return "UNDEFINED";
399     }
400 
401     /**
402      * Get the identifier of this activity. The activity identifier is unique for this GWESEngine.
403      *
404      * @return The identifier of this activity
405      */
406     public String getID() {
407         return ID;
408     }
409 
410     /**
411      * Get the parent workflow handler that owns this activity.
412      *
413      * @return The workflow handler.
414      */
415     public GenericWorkflowHandler getHandler() {
416         return handler;
417     }
418 
419     /**
420      * Get the GWESLogger for this activity.
421      * @return The GWESLogger.
422      */
423     public GWESLogger getGlog() {
424         return glog;
425     }
426 
427     /**
428      * Get transition occurrence object.
429      * @return
430      */
431     public TransitionOccurrence getTransitionOccurrence() {
432         return to;
433     }
434 
435     /**
436      * Get the operation candidate.
437      * @return
438      */
439     public OperationCandidate getOperationCandidate() {
440         return op;
441     }
442 
443     /**
444      * Get the operation name of this activity.
445      * @return The operation name.
446      */
447     public String getOperationName() {
448         return op.getOperationName();
449     }
450 
451     /**
452      * Get the resource name of this activity.
453      * @return The resource name.
454      */
455     public String getResourceName() {
456         return op.getResourceName();
457     }
458 
459     /**
460      * Get the timeoutRunning of this activity.
461      * @return The timeoutRunning in milliseconds.
462      */
463     public long getTimeoutRunning() {
464         return timeoutRunning;
465     }
466 
467     /**
468      * Set the timeoutRunning of this Activity.
469      * @param timeoutRunning The timout in milliseconds.
470      */
471     protected void setTimeoutRunning(long timeoutRunning) {
472         this.timeoutRunning = timeoutRunning;
473     }
474 
475     /**
476      * Get the timeoutActive of this activity.
477      * @return The timeoutActive in milliseconds.
478      */
479     public long getTimeoutActive() {
480         return timeoutActive;
481     }
482 
483     /**
484      * Set the timeoutActive of this Activity.
485      * @param timeoutActive The timout in milliseconds.
486      */
487     protected void setTimeoutActive(long timeoutActive) {
488         this.timeoutActive = timeoutActive;
489     }
490 
491     /**
492      * Gest the human-readable fault message. Returns <code>null</code> if there is no fault message.
493      * @return The fault message as String.
494      */
495     public String getFaultMessage() {
496         if (faultMessage == null && soapFault != null) {
497             faultMessage = HTMLFilter.filter(soapFault.toTokenString());
498         }
499         return faultMessage;
500     }
501 
502     /**
503      * Set the human-readable fault message.
504      * @param faultMessage The fault message as String.
505      */
506     public void setFaultMessage(String faultMessage) {
507         this.faultMessage = faultMessage;
508     }
509 
510     /**
511      * Append the human-readable fault message.
512      * @param faultMessage The fault message as String to append.
513      */
514     public void appendFaultMessage(String faultMessage) {
515         if (this.faultMessage == null || this.faultMessage.length() == 0) {
516             this.faultMessage = faultMessage;
517         } else {
518             this.faultMessage += "\n" + faultMessage;
519         }
520     }
521 
522     /**
523      * Get fault as SOAPFault.
524      * @return The SOAP fault of this activity or <code>null</code> if there is no fault.
525      */
526     public synchronized SOAPFault getSoapFault() {
527         if (soapFault == null && faultMessage != null) {
528             SOAPFault fault = new SOAPFault("env:Server", "Activity '" + getID() + "': Exception during activity invocation");
529             if (op != null) {
530                 fault.setNode(op.getResourceName().startsWith("hardware:") ? op.getResourceName().split(":")[1] : op.getResourceName());
531             }
532             if (this.lastExitCode != null) fault.setExitCode(this.lastExitCode);
533             fault.setDetail(toString() + "\n" + getFaultMessage());
534             setSoapFault(fault);
535         }
536         return soapFault;
537     }
538 
539     /**
540      * Set SOAP fault.
541      * @param soapFault The fault.
542      */
543     public void setSoapFault(SOAPFault soapFault) {
544         this.soapFault = soapFault;
545     }
546 
547     /**
548      * Get the start time of this activity in milliseconds since 1970.
549      * @return The start time
550      */
551     public long getStartTimeTotal() {
552         return startTimeTotal;
553     }
554 
555     /**
556      * Get the duration of how long this activity was "UNDEFINED" (before initiated).
557      *
558      * @return The duration in milliseconds
559      */
560     public long getDurationUndefined() {
561         return durationUndefined;
562     }
563 
564     /**
565      * Get the duration of how long this activity was "INITIATED" (before running).
566      *
567      * @return The duration in milliseconds
568      */
569     public long getDurationInitiated() {
570         return durationInitiated;
571     }
572 
573     /**
574      * Get the duration of how long this activity was "RUNNING" not including the time it was "ACTIVE".
575      *
576      * @return The duration in milliseconds
577      */
578     public long getDurationRunning() {
579         return durationRunning;
580     }
581 
582     /**
583      * Get the duration of how long this activity was "ACTIVE".
584      *
585      * @return The duration in milliseconds
586      */
587     public long getDurationActive() {
588         return durationActive;
589     }
590 
591     /**
592      * Get the duration of how long this activity was "SUSPENDED".
593      *
594      * @return The duration in milliseconds
595      */
596     public long getDurationSuspended() {
597         return durationSuspended;
598     }
599 
600     /**
601      * Get the total activity duration.
602      *
603      * @return The duration in milliseconds
604      */
605     public long getDurationTotal() {
606         return durationTotal;
607     }
608 
609     /**
610      * Get the number of pending attempts for this activity.
611      * "1" means, that this is the last attempt.
612      * @return number of attempts or "-1" if attempts is not set.
613      */
614     public synchronized int getAttempts() {
615         return attempts;
616     }
617 
618     public synchronized void decreaseAttempts() {
619         attempts--;
620     }
621 
622     /**
623      * Enqueue this activity in order to be processed by the ActivityStarter.
624      * If the workflow handler is <code>null</code>, then directly start the activity. 
625      */
626     public void enqueueActivity() throws WorkflowFormatException {
627         if (status == Status.INITIATED) {
628             if (handler != null) {
629                 handler.getEngine().getActivityQueue().enqueue(this);
630             } else {
631                 logger.error("Trying to enqueue activity without handler.");
632             }
633         } else {
634             logger.error("Wrong status for enqueue activity " + getStatusAsString());
635         }
636     }
637 
638     /**
639      * Get the activity status as an array of strings.
640      * Example:
641      * <pre>
642      * ///ToDo update regarding the real output
643      * "ID=hoheisel_f2968050-1d6a-11db-bacc-ad353bc1f9b1"
644      * "status=COMPLETED"
645      * "birthdayMs=1154003111126"
646      * "durationUndefinedMs=527"
647      * "durationInitiatedMs=792"
648      * "durationRunningMs=0"
649      * "durationActiveMs=0"
650      * "durationSuspendedMs=0"
651      * "durationTotalMs=1351"
652      * "endTimeMs=1154003112477"
653      * "level=MEMORY"
654      * "description=test workflow"
655      * </pre>
656      * @return The current activity status as string array
657      */
658     public String[] getStatusArray() {
659         String[] ret = new String[15];
660         int i=0;
661         ret[i++] = "ID="+getID();
662         ret[i++] = "status="+getStatusAsString();
663         ret[i++] = "activityClass=" + getClass().getName();
664         ret[i++] = "operationName=" + getOperationName();
665         ret[i++] = "resourceName=" + getResourceName();
666         ret[i++] = "timeoutActive=" + getTimeoutActive();
667         ret[i++] = "timeoutRunning=" + getTimeoutRunning();
668         ret[i++] = "birthdayMs="+getStartTimeTotal();
669         ret[i++] = "durationUndefinedMs=" + getDurationUndefined();
670         ret[i++] = "durationInitiatedMs=" + getDurationInitiated();
671         ret[i++] = "durationRunningMs=" + getDurationRunning();
672         ret[i++] = "durationActiveMs=" + getDurationActive();
673         ret[i++] = "durationSuspendedMs=" + getDurationSuspended();
674         ret[i++] = "durationTotalMs=" + getDurationTotal();
675         ret[i++] = "endTimeMs=" + (getDurationTotal() == 0L ? 0L : getStartTimeTotal()+getDurationTotal());
676         return ret;
677     }
678 
679     private void logStatistics() {
680         if (logger.isDebugEnabled()) {
681             logger.debug("UNDEFINED: " + getDurationUndefined() / 1000.0 + "s");
682             logger.debug("INITIATED: " + getDurationInitiated() / 1000.0 + "s");
683             logger.debug("RUNNING  : " + getDurationRunning() / 1000.0 + "s");
684             logger.debug("ACTIVE   : " + getDurationActive() / 1000.0 + "s");
685             logger.debug("SUSPENDED: " + getDurationSuspended() / 1000.0 + "s");
686             logger.debug("TOTAL    : " + getDurationTotal() / 1000.0 + "s");
687         }
688     }
689 
690     private void setAttempts() throws WorkflowFormatException {
691 
692         if (to != null && to.transition != null) {
693 
694             // if transition has failed before, check for maxattempts property in transition occurrence
695             String hasFailedProperty = to.transition.getProperties().get(Constants.PROP_TRANSITION_ACTIVITY_HAS_FAILED);
696             if (hasFailedProperty != null && hasFailedProperty.equals(Constants.TRUE)) {
697                 String attemptsStr = to.getProps().get(Constants.PROP_TOKEN_ACTIVITY_MAXATTEMPTS);
698                 if (attemptsStr != null) {
699                     try {
700                         attempts = Integer.parseInt(attemptsStr);
701                         return;
702                     } catch (NumberFormatException e) {
703                         throw new WorkflowFormatException("Wrong format of token property \""+Constants.PROP_TOKEN_ACTIVITY_MAXATTEMPTS+"\": " + e, e);
704                     }
705                 }
706             }
707 
708             // try to get attempts from transition property
709             String attemptsStr = to.transition.getProperties().get(Constants.PROP_TRANSITION_ACTIVITY_MAXATTEMPTS);
710             if (attemptsStr != null) {
711                 try {
712                     attempts = Integer.parseInt(attemptsStr);
713                     return;
714                 } catch (NumberFormatException e) {
715                     throw new WorkflowFormatException("Wrong format of transition property \""+Constants.PROP_TRANSITION_ACTIVITY_MAXATTEMPTS+"\": " + e, e);
716                 }
717             }
718         }
719 
720         // try to get attempts from workflow property
721         if (handler != null && handler.getWorkflow() != null) {
722             Object wfObj = handler.getWorkflow();
723             if (wfObj instanceof Workflow) {
724                 String attemptsStr = ((Workflow) wfObj).getProperties().get(Constants.PROP_ACTIVITY_MAXATTEMPTS);
725                 if (attemptsStr != null) {
726                     try {
727                         attempts = Integer.parseInt(attemptsStr);
728                         return;
729                     } catch (NumberFormatException e) {
730                         throw new WorkflowFormatException("Wrong format of workflow property \""+Constants.PROP_TRANSITION_ACTIVITY_MAXATTEMPTS+"\": " + e, e);
731                     }
732                 }
733             }
734         }
735 
736         // try to get attempts from system properties
737         String attemptsStr = System.getProperty(Constants.PROP_SYSTEM_GWES_ACTIVITY_MAXATTEMPTS);
738         if (attemptsStr != null) {
739             try {
740                 attempts = Integer.parseInt(attemptsStr);
741                 return;
742             } catch (NumberFormatException e) {
743                 throw new WorkflowFormatException("Wrong format of property \""+Constants.PROP_SYSTEM_GWES_ACTIVITY_MAXATTEMPTS+"\" in gwes.properties: " + e, e);
744             }
745         }
746 
747         // default
748         attempts = 1;
749     }
750 
751     private void setActivityTimeouts() throws WorkflowFormatException {
752         // no to => no transition => nothing to set.
753         if (to == null) {
754             logger.warn("setActivityTimeouts(): Transition occurrence is null for "+ID);
755             return;
756         }
757 
758         // set activity timeouts
759         String timeoutProperty = to.transition.getProperties().get(Constants.PROP_TRANSITION_TIMEOUT);
760         String timeoutRunningProperty = to.transition.getProperties().get(Constants.PROP_TRANSITION_TIMEOUT_RUNNING);
761         String timeoutActiveProperty = to.transition.getProperties().get(Constants.PROP_TRANSITION_TIMEOUT_ACTIVE);
762         // timeout.running -> timeout -> gwes.activity.timeout.running -> DEFAULT_TIMEOUT_RUNNING
763         if (timeoutRunningProperty != null) {
764             try {
765                 setTimeoutRunning(Long.parseLong(timeoutRunningProperty));
766             } catch (NumberFormatException e) {
767                 throw new WorkflowFormatException("Wrong format of property \"timeout.running\": " + e, e);
768             }
769         } else if (timeoutProperty != null) {
770             try {
771                 setTimeoutRunning(Long.parseLong(timeoutProperty));
772             } catch (NumberFormatException e) {
773                 throw new WorkflowFormatException("Wrong format of property \"timeout\": " + e, e);
774             }
775         } else {
776             setTimeoutRunning(Long.parseLong(System.getProperty(Constants.PROP_SYSTEM_GWES_ACTIVITY_TIMEOUT_RUNNING, "" + Activity.DEFAULT_TIMEOUT_RUNNING)));
777         }
778 
779         // timeout.active -> timeout -> gwes.activity.timeout.active -> DEFAULT_TIMEOUT_ACTIVE
780         if (timeoutActiveProperty != null) {
781             try {
782                 setTimeoutActive(Long.parseLong(timeoutActiveProperty));
783             } catch (NumberFormatException e) {
784                 throw new WorkflowFormatException("Wrong format of property \"timeout.active\": " + e, e);
785             }
786         } else if (timeoutProperty != null) {
787             try {
788                 setTimeoutActive(Long.parseLong(timeoutProperty));
789             } catch (NumberFormatException e) {
790                 throw new WorkflowFormatException("Wrong format of property \"timeout\": " + e, e);
791             }
792         } else {
793             setTimeoutActive(Long.parseLong(System.getProperty(Constants.PROP_SYSTEM_GWES_ACTIVITY_TIMEOUT_ACTIVE, "" + Activity.DEFAULT_TIMEOUT_ACTIVE)));
794         }
795     }
796 
797     // a concrete implementation should override the following methods
798 
799     /**
800      * Initiate this activity. Status should switch to INITIATED. Method should only work if the status was
801      * UNDEFINED before. Implement this method in all derived classes!
802      */
803     public abstract void initiateActivity() throws ActivityException;
804 
805     /**
806      * Start this activity. Status should switch to RUNNING. Implement this method in all derived classes!
807      */
808     public abstract void startActivity() throws ActivityException, WorkflowFormatException;
809 
810     /**
811      * Simulate this activity. Status should switch to RUNNING.
812      * This method is equivalent to "startActivity()", however the invocation of the activity is only simulated and
813      * takes minimum amount of time. The performance results (duration, memory consumption etc.) of the activity should
814      * be returned as properties of the output tokens.
815      * Implement this method in all derived classes!
816      */
817     public abstract void simulateActivity() throws ActivityException, WorkflowFormatException;
818 
819     /**
820      * Suspend this activity. Status should switch to SUSPENDED. Implement this method in all derived classes!
821      */
822     public abstract void suspendActivity() throws ActivityException;
823 
824     /**
825      * Resume this activity. Status should switch to RUNNING. Implement this method in all derived classes!
826      */
827     public abstract void resumeActivity() throws ActivityException;
828 
829     /**
830      * Abort this activity. Status should switch to TERMINATED. Implement this method in all derived classes!
831      */
832     public abstract void abortActivity() throws ActivityException;
833 
834     /**
835      * Restart this activity. Status should switch to INITIATED. Implement this method in all derived classes!
836      */
837     public abstract void restartActivity() throws ActivityException;
838 
839     /**
840      * Cleans up all temporary data produced by this activity. Implement this method in all derived classes!
841      */
842     public abstract void cleanupActivity() throws ActivityException;
843 
844     /**
845      * Each activity should be able to give some information about itself using the toString() method.
846      * @return String containing human readable information about the activity.
847      */
848     public String toString() {
849         StringBuffer buffer = new StringBuffer();
850         buffer.append(getClass().getName()).append('[');
851         buffer.append("ID=").append(ID);
852         if (to != null) {
853             buffer.append("; ").append(to);
854         }
855         if (op != null) {
856             buffer.append("; OperationName=").append(op.getOperationName());
857             buffer.append("; ResourceName=").append(op.getResourceName());
858         }
859         buffer.append(']');
860         return buffer.toString();
861     }
862 
863     /**
864      * Put SOAP Fault on each output token that is connected with a edge expression.
865      * Does NOT overwrite existing tokens!
866      * @throws net.kwfgrid.gworkflowdl.structure.WorkflowFormatException
867      */
868     protected void putFaultTokenOnOutputPlaces() throws WorkflowFormatException {
869         if (to == null) return;
870         logger.debug("putting one fault token on each of the output places...");
871 
872         for (TokenParameter tp : to.tokens) {
873             switch (tp.scope) {
874                 case OUTPUT:
875                     if (tp.token != null) continue;
876                     String edgeExpression = tp.edge.getExpression();
877                     if (edgeExpression != null && edgeExpression.length() > 0) {
878                         try {
879                             tp.token = getSoapFault().toToken();
880                         } catch (NullPointerException e) {
881                             logger.error("NullPointerException during putting fault tokens on output places: "+e,e);
882                         }
883                     }
884                     break;
885             }
886         }
887     }
888 
889 }