001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.oozie;
020
021import java.io.DataInput;
022import java.io.DataOutput;
023import java.io.IOException;
024import java.sql.Timestamp;
025import java.text.MessageFormat;
026import java.util.Date;
027import java.util.List;
028import java.util.Properties;
029
030import javax.persistence.Basic;
031import javax.persistence.Column;
032import javax.persistence.Entity;
033import javax.persistence.Id;
034import javax.persistence.Lob;
035import javax.persistence.NamedQueries;
036import javax.persistence.NamedQuery;
037import javax.persistence.Table;
038
039import org.apache.hadoop.io.Writable;
040import org.apache.oozie.client.WorkflowAction;
041import org.apache.oozie.client.rest.JsonBean;
042import org.apache.oozie.client.rest.JsonTags;
043import org.apache.oozie.client.rest.JsonUtils;
044import org.apache.oozie.util.DateUtils;
045import org.apache.oozie.util.ParamChecker;
046import org.apache.oozie.util.PropertiesUtils;
047import org.apache.oozie.util.WritableUtils;
048import org.apache.openjpa.persistence.jdbc.Index;
049import org.apache.openjpa.persistence.jdbc.Strategy;
050
051import org.json.simple.JSONArray;
052import org.json.simple.JSONObject;
053
054/**
055 * Bean that contains all the information to start an action for a workflow
056 * node.
057 */
058@Entity
059@NamedQueries({
060
061    @NamedQuery(name = "UPDATE_ACTION", query = "update WorkflowActionBean a set a.conf = :conf, a.consoleUrl = :consoleUrl, a.data = :data, a.stats = :stats, a.externalChildIDs = :externalChildIDs, a.errorCode = :errorCode, a.errorMessage = :errorMessage, a.externalId = :externalId, a.externalStatus = :externalStatus, a.name = :name, a.cred = :cred , a.retries = :retries, a.trackerUri = :trackerUri, a.transition = :transition, a.type = :type, a.endTimestamp = :endTime, a.executionPath = :executionPath, a.lastCheckTimestamp = :lastCheckTime, a.logToken = :logToken, a.pending = :pending, a.pendingAgeTimestamp = :pendingAge, a.signalValue = :signalValue, a.slaXml = :slaXml, a.startTimestamp = :startTime, a.statusStr = :status, a.wfId=:wfId where a.id = :id"),
062
063    @NamedQuery(name = "UPDATE_ACTION_FOR_LAST_CHECKED_TIME", query = "update WorkflowActionBean a set a.lastCheckTimestamp = :lastCheckTime where a.id = :id"),
064
065    @NamedQuery(name = "UPDATE_ACTION_START", query = "update WorkflowActionBean a set a.startTimestamp = :startTime, a.externalChildIDs = :externalChildIDs, a.conf = :conf, a.errorCode = :errorCode, a.errorMessage = :errorMessage, a.startTimestamp = :startTime, a.externalId = :externalId, a.trackerUri = :trackerUri, a.consoleUrl = :consoleUrl, a.lastCheckTimestamp = :lastCheckTime, a.statusStr = :status, a.externalStatus = :externalStatus, a.data = :data, a.retries = :retries, a.pending = :pending, a.pendingAgeTimestamp = :pendingAge, a.userRetryCount = :userRetryCount where a.id = :id"),
066
067    @NamedQuery(name = "UPDATE_ACTION_CHECK", query = "update WorkflowActionBean a set a.userRetryCount = :userRetryCount, a.stats = :stats, a.externalChildIDs = :externalChildIDs, a.externalStatus = :externalStatus, a.statusStr = :status, a.data = :data, a.pending = :pending, a.errorCode = :errorCode, a.errorMessage = :errorMessage, a.lastCheckTimestamp = :lastCheckTime, a.retries = :retries, a.pendingAgeTimestamp = :pendingAge, a.startTimestamp = :startTime where a.id = :id"),
068
069    @NamedQuery(name = "UPDATE_ACTION_END", query = "update WorkflowActionBean a set a.stats = :stats, a.errorCode = :errorCode, a.errorMessage = :errorMessage, a.retries = :retries, a.endTimestamp = :endTime, a.statusStr = :status, a.pending = :pending, a.pendingAgeTimestamp = :pendingAge, a.signalValue = :signalValue, a.userRetryCount = :userRetryCount, a.externalStatus = :externalStatus where a.id = :id"),
070
071    @NamedQuery(name = "UPDATE_ACTION_PENDING", query = "update WorkflowActionBean a set a.pending = :pending, a.pendingAgeTimestamp = :pendingAge, a.executionPath = :executionPath where a.id = :id"),
072
073    @NamedQuery(name = "UPDATE_ACTION_STATUS_PENDING", query = "update WorkflowActionBean a set a.statusStr = :status, a.pending = :pending, a.pendingAgeTimestamp = :pendingAge where a.id = :id"),
074
075    @NamedQuery(name = "UPDATE_ACTION_PENDING_TRANS", query = "update WorkflowActionBean a set a.pending = :pending, a.pendingAgeTimestamp = :pendingAge, a.transition = :transition where a.id = :id"),
076
077    @NamedQuery(name = "UPDATE_ACTION_PENDING_TRANS_ERROR", query = "update WorkflowActionBean a set a.pending = :pending, a.pendingAgeTimestamp = :pendingAge, a.transition = :transition, a.errorCode = :errorCode, a.errorMessage = :errorMessage where a.id = :id"),
078
079    @NamedQuery(name = "DELETE_ACTION", query = "delete from WorkflowActionBean a where a.id IN (:id)"),
080
081    @NamedQuery(name = "DELETE_ACTIONS_FOR_WORKFLOW", query = "delete from WorkflowActionBean a where a.wfId IN (:wfId)"),
082
083    @NamedQuery(name = "GET_ACTIONS", query = "select OBJECT(a) from WorkflowActionBean a"),
084
085    @NamedQuery(name = "GET_ACTION", query = "select OBJECT(a) from WorkflowActionBean a where a.id = :id"),
086
087    @NamedQuery(name = "GET_ACTION_ID_TYPE_LASTCHECK", query = "select a.id, a.type, a.lastCheckTimestamp from WorkflowActionBean a where a.id = :id"),
088
089    @NamedQuery(name = "GET_ACTION_FAIL", query = "select a.id, a.wfId, a.name, a.statusStr, a.pending, a.type, a.logToken, a.transition, a.errorCode, a.errorMessage from WorkflowActionBean a where a.id = :id"),
090
091    @NamedQuery(name = "GET_ACTION_SIGNAL", query = "select a.id, a.wfId, a.name, a.statusStr, a.pending, a.pendingAgeTimestamp, a.type, a.logToken, a.transition, a.errorCode, a.errorMessage, a.executionPath, a.signalValue, a.slaXml, a.externalId from WorkflowActionBean a where a.id = :id"),
092
093    @NamedQuery(name = "GET_ACTION_CHECK", query = "select a.id, a.wfId, a.name, a.statusStr, a.pending, a.pendingAgeTimestamp, a.type, a.logToken, a.transition, a.retries, a.userRetryCount, a.userRetryMax, a.userRetryInterval, a.trackerUri, a.startTimestamp, a.endTimestamp, a.lastCheckTimestamp, a.errorCode, a.errorMessage, a.externalId, a.externalStatus, a.externalChildIDs, a.conf from WorkflowActionBean a where a.id = :id"),
094
095    @NamedQuery(name = "GET_ACTION_END", query = "select a.id, a.wfId, a.name, a.statusStr, a.pending, a.pendingAgeTimestamp, a.type, a.logToken, a.transition, a.retries, a.trackerUri, a.userRetryCount, a.userRetryMax, a.userRetryInterval, a.startTimestamp, a.endTimestamp, a.errorCode, a.errorMessage, a.externalId, a.externalStatus, a.externalChildIDs, a.conf, a.data, a.stats from WorkflowActionBean a where a.id = :id"),
096
097    @NamedQuery(name = "GET_ACTION_COMPLETED", query = "select a.id, a.wfId, a.statusStr, a.type, a.logToken from WorkflowActionBean a where a.id = :id"),
098
099    @NamedQuery(name = "GET_ACTION_FOR_UPDATE", query = "select OBJECT(a) from WorkflowActionBean a where a.id = :id"),
100
101    @NamedQuery(name = "GET_ACTION_FOR_SLA", query = "select a.id, a.statusStr, a.startTimestamp, a.endTimestamp from WorkflowActionBean a where a.id = :id"),
102
103    @NamedQuery(name = "GET_ACTIONS_FOR_WORKFLOW", query = "select OBJECT(a) from WorkflowActionBean a where a.wfId = :wfId order by a.startTimestamp"),
104
105    @NamedQuery(name = "GET_ACTIONS_OF_WORKFLOW_FOR_UPDATE", query = "select OBJECT(a) from WorkflowActionBean a where a.wfId = :wfId order by a.startTimestamp"),
106
107    @NamedQuery(name = "GET_PENDING_ACTIONS", query = "select a.id, a.wfId, a.statusStr, a.type, a.pendingAgeTimestamp from WorkflowActionBean a where a.pending = 1 AND a.pendingAgeTimestamp < :pendingAge AND a.statusStr <> 'RUNNING' AND a.createdTimeTS >= :createdTime"),
108
109    @NamedQuery(name = "GET_RUNNING_ACTIONS", query = "select a.id from WorkflowActionBean a where a.pending = 1 AND a.statusStr = 'RUNNING' AND a.lastCheckTimestamp < :lastCheckTime"),
110
111    @NamedQuery(name = "GET_RETRY_MANUAL_ACTIONS", query = "select OBJECT(a) from WorkflowActionBean a where a.wfId = :wfId AND (a.statusStr = 'START_RETRY' OR a.statusStr = 'START_MANUAL' OR a.statusStr = 'END_RETRY' OR a.statusStr = 'END_MANUAL')"),
112
113    @NamedQuery(name = "GET_ACTIONS_FOR_WORKFLOW_RERUN", query = "select a.id, a.name, a.statusStr, a.endTimestamp, a.type from WorkflowActionBean a where a.wfId = :wfId order by a.startTimestamp") })
114@Table(name = "WF_ACTIONS")
115public class WorkflowActionBean implements Writable, WorkflowAction, JsonBean {
116    @Id
117    private String id;
118
119    @Basic
120    @Index
121    @Column(name = "wf_id")
122    private String wfId = null;
123
124    @Basic
125    @Column(name = "created_time")
126    private Timestamp createdTimeTS = null;
127
128    @Basic
129    @Index
130    @Column(name = "status")
131    private String statusStr = WorkflowAction.Status.PREP.toString();
132
133    @Basic
134    @Column(name = "last_check_time")
135    private Timestamp lastCheckTimestamp;
136
137    @Basic
138    @Column(name = "end_time")
139    private Timestamp endTimestamp = null;
140
141    @Basic
142    @Column(name = "start_time")
143    private Timestamp startTimestamp = null;
144
145    @Basic
146    @Column(name = "execution_path", length = 1024)
147    private String executionPath = null;
148
149    @Basic
150    @Column(name = "pending")
151    private int pending = 0;
152
153    @Basic
154    @Index
155    @Column(name = "pending_age")
156    private Timestamp pendingAgeTimestamp = null;
157
158    @Basic
159    @Column(name = "signal_value")
160    private String signalValue = null;
161
162    @Basic
163    @Column(name = "log_token")
164    private String logToken = null;
165
166    @Basic
167    @Column(name = "sla_xml")
168    @Lob
169    @Strategy("org.apache.oozie.executor.jpa.StringBlobValueHandler")
170    private StringBlob slaXml;
171
172    @Basic
173    @Column(name = "name")
174    private String name = null;
175
176    @Basic
177    @Column(name = "cred")
178    private String cred = null;
179
180    @Basic
181    @Column(name = "type")
182    private String type = null;
183
184    @Basic
185    @Column(name = "conf")
186    @Lob
187    @Strategy("org.apache.oozie.executor.jpa.StringBlobValueHandler")
188    private StringBlob conf;
189
190    @Basic
191    @Column(name = "retries")
192    private int retries;
193
194    @Basic
195    @Column(name = "user_retry_count")
196    private int userRetryCount;
197
198    @Basic
199    @Column(name = "user_retry_max")
200    private int userRetryMax;
201
202    @Basic
203    @Column(name = "user_retry_interval")
204    private int userRetryInterval;
205
206    @Basic
207    @Column(name = "transition")
208    private String transition = null;
209
210    @Basic
211    @Column(name = "data")
212    @Lob
213    @Strategy("org.apache.oozie.executor.jpa.StringBlobValueHandler")
214    private StringBlob data;
215
216    @Basic
217    @Column(name = "stats")
218    @Lob
219    @Strategy("org.apache.oozie.executor.jpa.StringBlobValueHandler")
220    private StringBlob stats;
221
222    @Basic
223    @Column(name = "external_child_ids")
224    @Lob
225    @Strategy("org.apache.oozie.executor.jpa.StringBlobValueHandler")
226    private StringBlob externalChildIDs;
227
228    @Basic
229    @Column(name = "external_id")
230    private String externalId = null;
231
232    @Basic
233    @Column(name = "external_status")
234    private String externalStatus = null;
235
236    @Basic
237    @Column(name = "tracker_uri")
238    private String trackerUri = null;
239
240    @Basic
241    @Column(name = "console_url")
242    private String consoleUrl = null;
243
244    @Basic
245    @Column(name = "error_code")
246    private String errorCode = null;
247
248    @Column(name = "error_message", length = 500)
249    private String errorMessage = null;
250
251    /**
252     * Default constructor.
253     */
254    public WorkflowActionBean() {
255    }
256
257    /**
258     * Serialize the action bean to a data output.
259     *
260     * @param dataOutput data output.
261     * @throws IOException thrown if the action bean could not be serialized.
262     */
263
264    public void write(DataOutput dataOutput) throws IOException {
265        WritableUtils.writeStr(dataOutput, getId());
266        WritableUtils.writeStr(dataOutput, getName());
267        WritableUtils.writeStr(dataOutput, getCred());
268        WritableUtils.writeStr(dataOutput, getType());
269        WritableUtils.writeStr(dataOutput, getConf());
270        WritableUtils.writeStr(dataOutput, getStatusStr());
271        dataOutput.writeInt(getRetries());
272        dataOutput.writeLong((getStartTime() != null) ? getStartTime().getTime() : -1);
273        dataOutput.writeLong((getEndTime() != null) ? getEndTime().getTime() : -1);
274        dataOutput.writeLong((getLastCheckTime() != null) ? getLastCheckTime().getTime() : -1);
275        WritableUtils.writeStr(dataOutput, getTransition());
276        WritableUtils.writeStr(dataOutput, getData());
277        WritableUtils.writeStr(dataOutput, getStats());
278        WritableUtils.writeStr(dataOutput, getExternalChildIDs());
279        WritableUtils.writeStr(dataOutput, getExternalId());
280        WritableUtils.writeStr(dataOutput, getExternalStatus());
281        WritableUtils.writeStr(dataOutput, getTrackerUri());
282        WritableUtils.writeStr(dataOutput, getConsoleUrl());
283        WritableUtils.writeStr(dataOutput, getErrorCode());
284        WritableUtils.writeStr(dataOutput, getErrorMessage());
285        WritableUtils.writeStr(dataOutput, wfId);
286        WritableUtils.writeStr(dataOutput, executionPath);
287        dataOutput.writeInt(pending);
288        dataOutput.writeLong((getPendingAge() != null) ? getPendingAge().getTime() : -1);
289        WritableUtils.writeStr(dataOutput, signalValue);
290        WritableUtils.writeStr(dataOutput, logToken);
291        dataOutput.writeInt(getUserRetryCount());
292        dataOutput.writeInt(getUserRetryInterval());
293        dataOutput.writeInt(getUserRetryMax());
294    }
295
296    /**
297     * Deserialize an action bean from a data input.
298     *
299     * @param dataInput data input.
300     * @throws IOException thrown if the action bean could not be deserialized.
301     */
302    public void readFields(DataInput dataInput) throws IOException {
303        setId(WritableUtils.readStr(dataInput));
304        setName(WritableUtils.readStr(dataInput));
305        setCred(WritableUtils.readStr(dataInput));
306        setType(WritableUtils.readStr(dataInput));
307        setConf(WritableUtils.readStr(dataInput));
308        setStatus(WorkflowAction.Status.valueOf(WritableUtils.readStr(dataInput)));
309        setRetries(dataInput.readInt());
310        long d = dataInput.readLong();
311        if (d != -1) {
312            setStartTime(new Date(d));
313        }
314        d = dataInput.readLong();
315        if (d != -1) {
316            setEndTime(new Date(d));
317        }
318        d = dataInput.readLong();
319        if (d != -1) {
320            setLastCheckTime(new Date(d));
321        }
322        setTransition(WritableUtils.readStr(dataInput));
323        setData(WritableUtils.readStr(dataInput));
324        setStats(WritableUtils.readStr(dataInput));
325        setExternalChildIDs(WritableUtils.readStr(dataInput));
326        setExternalId(WritableUtils.readStr(dataInput));
327        setExternalStatus(WritableUtils.readStr(dataInput));
328        setTrackerUri(WritableUtils.readStr(dataInput));
329        setConsoleUrl(WritableUtils.readStr(dataInput));
330        setErrorInfo(WritableUtils.readStr(dataInput), WritableUtils.readStr(dataInput));
331        wfId = WritableUtils.readStr(dataInput);
332        executionPath = WritableUtils.readStr(dataInput);
333        pending = dataInput.readInt();
334        d = dataInput.readLong();
335        if (d != -1) {
336            pendingAgeTimestamp = DateUtils.convertDateToTimestamp(new Date(d));
337        }
338        signalValue = WritableUtils.readStr(dataInput);
339        logToken = WritableUtils.readStr(dataInput);
340        setUserRetryCount(dataInput.readInt());
341        setUserRetryInterval(dataInput.readInt());
342        setUserRetryMax(dataInput.readInt());
343    }
344
345    /**
346     * Return whether workflow action in terminal state or not
347     *
348     * @return
349     */
350    public boolean inTerminalState() {
351        boolean isTerminalState = false;
352        switch (WorkflowAction.Status.valueOf(statusStr)) {
353            case ERROR:
354            case FAILED:
355            case KILLED:
356            case OK:
357                isTerminalState = true;
358                break;
359            default:
360                break;
361        }
362        return isTerminalState;
363    }
364
365    /**
366     * Return if the action execution is complete.
367     *
368     * @return if the action start is complete.
369     */
370    public boolean isExecutionComplete() {
371        return getStatus() == WorkflowAction.Status.DONE;
372    }
373
374    /**
375     * Return if the action is START_RETRY or START_MANUAL or END_RETRY or
376     * END_MANUAL.
377     *
378     * @return boolean true if status is START_RETRY or START_MANUAL or
379     *         END_RETRY or END_MANUAL
380     */
381    public boolean isRetryOrManual() {
382        return (getStatus() == WorkflowAction.Status.START_RETRY || getStatus() == WorkflowAction.Status.START_MANUAL
383                || getStatus() == WorkflowAction.Status.END_RETRY || getStatus() == WorkflowAction.Status.END_MANUAL);
384    }
385
386    /**
387     * Return true if the action is USER_RETRY
388     *
389     * @return boolean true if status is USER_RETRY
390     */
391    public boolean isUserRetry() {
392        return (getStatus() == WorkflowAction.Status.USER_RETRY);
393    }
394
395    /**
396     * Return if the action is complete.
397     *
398     * @return if the action is complete.
399     */
400    public boolean isComplete() {
401        return getStatus() == WorkflowAction.Status.OK || getStatus() == WorkflowAction.Status.KILLED
402                || getStatus() == WorkflowAction.Status.ERROR;
403    }
404
405    /**
406     * Return if the action is complete with failure.
407     *
408     * @return if the action is complete with failure.
409     */
410    public boolean isTerminalWithFailure() {
411        boolean result = false;
412        switch (getStatus()) {
413            case FAILED:
414            case KILLED:
415            case ERROR:
416                result = true;
417        }
418        return result;
419    }
420
421    /**
422     * Set the action pending flag to true.
423     */
424    public void setPendingOnly() {
425        pending = 1;
426    }
427
428    /**
429     * Set the action as pending and the current time as pending.
430     */
431    public void setPending() {
432        pending = 1;
433        pendingAgeTimestamp = DateUtils.convertDateToTimestamp(new Date());
434    }
435
436    /**
437     * Set pending flag
438     */
439    public void setPending(int i) {
440        pending = i;
441    }
442
443    /**
444     * Set a time when the action will be pending, normally a time in the
445     * future.
446     *
447     * @param pendingAge the time when the action will be pending.
448     */
449    public void setPendingAge(Date pendingAge) {
450        this.pendingAgeTimestamp = DateUtils.convertDateToTimestamp(pendingAge);
451    }
452
453    /**
454     * Return the pending age of the action.
455     *
456     * @return the pending age of the action, <code>null</code> if the action is
457     *         not pending.
458     */
459    public Date getPendingAge() {
460        return DateUtils.toDate(pendingAgeTimestamp);
461    }
462
463    /**
464     * Return if the action is pending.
465     *
466     * @return if the action is pending.
467     */
468    public boolean isPending() {
469        return pending == 1 ? true : false;
470    }
471
472    /**
473     * Removes the pending flag and pendingAge from the action.
474     */
475    public void resetPending() {
476        pending = 0;
477        pendingAgeTimestamp = null;
478    }
479
480    /**
481     * Removes the pending flag from the action.
482     */
483    public void resetPendingOnly() {
484        pending = 0;
485    }
486
487    /**
488     * Increments the number of retries for the action.
489     */
490    public void incRetries() {
491        setRetries(getRetries() + 1);
492    }
493
494    /**
495     * Set a tracking information for an action, and set the action status to
496     * {@link Action.Status#DONE}
497     *
498     * @param externalId external ID for the action.
499     * @param trackerUri tracker URI for the action.
500     * @param consoleUrl console URL for the action.
501     */
502    public void setStartData(String externalId, String trackerUri, String consoleUrl) {
503        setExternalId(ParamChecker.notEmpty(externalId, "externalId"));
504        setTrackerUri(ParamChecker.notEmpty(trackerUri, "trackerUri"));
505        setConsoleUrl(ParamChecker.notEmpty(consoleUrl, "consoleUrl"));
506        Date now = new Date();
507        if (this.startTimestamp == null) {
508            setStartTime(now);
509        }
510        setLastCheckTime(now);
511        setStatus(Status.RUNNING);
512    }
513
514    /**
515     * Set the completion information for an action start. Sets the Action
516     * status to {@link Action.Status#DONE}
517     *
518     * @param externalStatus action external end status.
519     * @param actionData action output data, <code>null</code> if there is no
520     *        action output data.
521     */
522    public void setExecutionData(String externalStatus, Properties actionData) {
523        setStatus(Status.DONE);
524        setExternalStatus(ParamChecker.notEmpty(externalStatus, "externalStatus"));
525        if (actionData != null) {
526            setData(PropertiesUtils.propertiesToString(actionData));
527        }
528    }
529
530    /**
531     * Return the action statistics info.
532     *
533     * @return Json representation of the stats.
534     */
535    public String getExecutionStats() {
536        return getStats();
537    }
538
539    /**
540     * Set the action statistics info for the workflow action.
541     *
542     * @param Json representation of the stats.
543     */
544    public void setExecutionStats(String jsonStats) {
545        setStats(jsonStats);
546    }
547
548    /**
549     * Return the external child IDs.
550     *
551     * @return externalChildIDs as a string.
552     */
553    @Override
554    public String getExternalChildIDs() {
555        return externalChildIDs == null ? null : externalChildIDs.getString();
556    }
557
558    /**
559     * Set the external child IDs for the workflow action.
560     *
561     * @param externalChildIDs as a string.
562     */
563    public void setExternalChildIDs(String externalChildIDs) {
564        if (this.externalChildIDs == null) {
565            this.externalChildIDs = new StringBlob(externalChildIDs);
566        }
567        else {
568            this.externalChildIDs.setString(externalChildIDs);
569        }
570    }
571
572    /**
573     * Set external child ids
574     *
575     * @param externalChildIds
576     */
577    public void setExternalChildIDsBlob(StringBlob externalChildIDs) {
578        this.externalChildIDs = externalChildIDs;
579    }
580
581    /**
582     * Get external ChildIds
583     *
584     * @return
585     */
586    public StringBlob getExternalChildIDsBlob() {
587        return externalChildIDs;
588    }
589
590    /**
591     * Set the completion information for an action end.
592     *
593     * @param status action status, {@link Action.Status#OK} or
594     *        {@link Action.Status#ERROR} or {@link Action.Status#KILLED}
595     * @param signalValue the signal value. In most cases, the value should be
596     *        OK or ERROR.
597     */
598    public void setEndData(Status status, String signalValue) {
599        if (status == null || (status != Status.OK && status != Status.ERROR && status != Status.KILLED)) {
600            throw new IllegalArgumentException("Action status must be OK, ERROR or KILLED. Received ["
601                    + status.toString() + "]");
602        }
603        if (status == Status.OK) {
604            setErrorInfo(null, null);
605        }
606        setStatus(status);
607        setSignalValue(ParamChecker.notEmpty(signalValue, "signalValue"));
608    }
609
610    /**
611     * Return the job Id.
612     *
613     * @return the job Id.
614     */
615    public String getJobId() {
616        return wfId;
617    }
618
619    /**
620     * Return the job Id.
621     *
622     * @return the job Id.
623     */
624    public String getWfId() {
625        return wfId;
626    }
627
628    /**
629     * Set the job id.
630     *
631     * @param id jobId;
632     */
633    public void setJobId(String id) {
634        this.wfId = id;
635    }
636
637    public void setSlaXml(String slaXmlStr) {
638        if (this.slaXml == null) {
639            this.slaXml = new StringBlob(slaXmlStr);
640        }
641        else {
642            this.slaXml.setString(slaXmlStr);
643        }
644    }
645
646    public String getSlaXml() {
647        return slaXml == null ? null : slaXml.getString();
648    }
649
650    public void setSlaXmlBlob(StringBlob slaXml) {
651        this.slaXml = slaXml;
652    }
653
654    public StringBlob getSlaXmlBlob() {
655        return slaXml;
656    }
657
658    /**
659     * Set status of job
660     *
661     * @param val
662     */
663    public void setStatus(Status val) {
664        this.statusStr = val.toString();
665    }
666
667    @Override
668    public Status getStatus() {
669        return Status.valueOf(this.statusStr);
670    }
671
672    /**
673     * Set status
674     *
675     * @param statusStr
676     */
677    public void setStatusStr(String statusStr) {
678        this.statusStr = statusStr;
679    }
680
681    /**
682     * Get status
683     *
684     * @return
685     */
686    public String getStatusStr() {
687        return statusStr;
688    }
689
690    /**
691     * Return the node execution path.
692     *
693     * @return the node execution path.
694     */
695    public String getExecutionPath() {
696        return executionPath;
697    }
698
699    /**
700     * Set the node execution path.
701     *
702     * @param executionPath the node execution path.
703     */
704    public void setExecutionPath(String executionPath) {
705        this.executionPath = executionPath;
706    }
707
708    /**
709     * Return the signal value for the action.
710     * <p/>
711     * For decision nodes it is the choosen transition, for actions it is OK or
712     * ERROR.
713     *
714     * @return the action signal value.
715     */
716    public String getSignalValue() {
717        return signalValue;
718    }
719
720    /**
721     * Set the signal value for the action.
722     * <p/>
723     * For decision nodes it is the choosen transition, for actions it is OK or
724     * ERROR.
725     *
726     * @param signalValue the action signal value.
727     */
728    public void setSignalValue(String signalValue) {
729        this.signalValue = signalValue;
730    }
731
732    /**
733     * Return the job log token.
734     *
735     * @return the job log token.
736     */
737    public String getLogToken() {
738        return logToken;
739    }
740
741    /**
742     * Set the job log token.
743     *
744     * @param logToken the job log token.
745     */
746    public void setLogToken(String logToken) {
747        this.logToken = logToken;
748    }
749
750    /**
751     * Return the action last check time
752     *
753     * @return the last check time
754     */
755    public Date getLastCheckTime() {
756        return DateUtils.toDate(lastCheckTimestamp);
757    }
758
759    /**
760     * Return the action last check time
761     *
762     * @return the last check time
763     */
764    public Timestamp getLastCheckTimestamp() {
765        return lastCheckTimestamp;
766    }
767
768    /**
769     * Return the action last check time
770     *
771     * @return the last check time
772     */
773    public Timestamp getStartTimestamp() {
774        return startTimestamp;
775    }
776
777    /**
778     * Return the action last check time
779     *
780     * @return the last check time
781     */
782    public Timestamp getEndTimestamp() {
783        return endTimestamp;
784    }
785
786    /**
787     * Return the action last check time
788     *
789     * @return the last check time
790     */
791    public Timestamp getPendingAgeTimestamp() {
792        return pendingAgeTimestamp;
793    }
794
795    /**
796     * Sets the action last check time
797     *
798     * @param lastCheckTime the last check time to set.
799     */
800    public void setLastCheckTime(Date lastCheckTime) {
801        this.lastCheckTimestamp = DateUtils.convertDateToTimestamp(lastCheckTime);
802    }
803
804    public int getPending() {
805        return this.pending;
806    }
807
808    @Override
809    public Date getStartTime() {
810        return DateUtils.toDate(startTimestamp);
811    }
812
813    /**
814     * Set start time
815     *
816     * @param startTime
817     */
818    public void setStartTime(Date startTime) {
819        this.startTimestamp = DateUtils.convertDateToTimestamp(startTime);
820    }
821
822    @Override
823    public Date getEndTime() {
824        return DateUtils.toDate(endTimestamp);
825    }
826
827    /**
828     * Set end time
829     *
830     * @param endTime
831     */
832    public void setEndTime(Date endTime) {
833        this.endTimestamp = DateUtils.convertDateToTimestamp(endTime);
834    }
835
836    @SuppressWarnings("unchecked")
837    public JSONObject toJSONObject() {
838        return toJSONObject("GMT");
839    }
840
841    @SuppressWarnings("unchecked")
842    public JSONObject toJSONObject(String timeZoneId) {
843        JSONObject json = new JSONObject();
844        json.put(JsonTags.WORKFLOW_ACTION_ID, id);
845        json.put(JsonTags.WORKFLOW_ACTION_NAME, name);
846        json.put(JsonTags.WORKFLOW_ACTION_AUTH, cred);
847        json.put(JsonTags.WORKFLOW_ACTION_TYPE, type);
848        json.put(JsonTags.WORKFLOW_ACTION_CONF, getConf());
849        json.put(JsonTags.WORKFLOW_ACTION_STATUS, statusStr);
850        json.put(JsonTags.WORKFLOW_ACTION_RETRIES, (long) retries);
851        json.put(JsonTags.WORKFLOW_ACTION_START_TIME, JsonUtils.formatDateRfc822(getStartTime(), timeZoneId));
852        json.put(JsonTags.WORKFLOW_ACTION_END_TIME, JsonUtils.formatDateRfc822(getEndTime(), timeZoneId));
853        json.put(JsonTags.WORKFLOW_ACTION_TRANSITION, transition);
854        json.put(JsonTags.WORKFLOW_ACTION_DATA, getData());
855        json.put(JsonTags.WORKFLOW_ACTION_STATS, getStats());
856        json.put(JsonTags.WORKFLOW_ACTION_EXTERNAL_CHILD_IDS, getExternalChildIDs());
857        json.put(JsonTags.WORKFLOW_ACTION_EXTERNAL_ID, externalId);
858        json.put(JsonTags.WORKFLOW_ACTION_EXTERNAL_STATUS, externalStatus);
859        json.put(JsonTags.WORKFLOW_ACTION_TRACKER_URI, trackerUri);
860        json.put(JsonTags.WORKFLOW_ACTION_CONSOLE_URL, consoleUrl);
861        json.put(JsonTags.WORKFLOW_ACTION_ERROR_CODE, errorCode);
862        json.put(JsonTags.WORKFLOW_ACTION_ERROR_MESSAGE, errorMessage);
863        json.put(JsonTags.TO_STRING, toString());
864        json.put(JsonTags.WORKFLOW_ACTION_USER_RETRY_INTERVAL, userRetryInterval);
865        json.put(JsonTags.WORKFLOW_ACTION_USER_RETRY_COUNT, userRetryCount);
866        json.put(JsonTags.WORKFLOW_ACTION_USER_RETRY_MAX, userRetryMax);
867        json.put(JsonTags.WORKFLOW_ACTION_CRED, cred);
868        return json;
869    }
870
871    @Override
872    public String getId() {
873        return id;
874    }
875
876    public void setId(String id) {
877        this.id = id;
878    }
879
880    public Timestamp getCreatedTimestamp() {
881        return createdTimeTS;
882    }
883
884    public Date getCreatedTime() {
885        return DateUtils.toDate(createdTimeTS);
886    }
887
888    public void setCreatedTime(Date createdTime) {
889        this.createdTimeTS = DateUtils.convertDateToTimestamp(createdTime);
890    }
891
892    @Override
893    public String getName() {
894        return name;
895    }
896
897    public void setName(String name) {
898        this.name = name;
899    }
900
901    @Override
902    public String getCred() {
903        return cred;
904    }
905
906    public void setCred(String cred) {
907        this.cred = cred;
908    }
909
910    @Override
911    public String getType() {
912        return type;
913    }
914
915    public void setType(String type) {
916        this.type = type;
917    }
918
919    @Override
920    public String getConf() {
921        return conf == null ? null : conf.getString();
922    }
923
924    public void setConf(String conf) {
925        if (this.conf == null) {
926            this.conf = new StringBlob(conf);
927        }
928        else {
929            this.conf.setString(conf);
930        }
931    }
932
933    public void setConfBlob(StringBlob conf) {
934        this.conf = conf;
935    }
936
937    public StringBlob getConfBlob() {
938        return conf;
939    }
940
941    @Override
942    public int getRetries() {
943        return retries;
944    }
945
946    public void setRetries(int retries) {
947        this.retries = retries;
948    }
949
950    @Override
951    public int getUserRetryCount() {
952        return userRetryCount;
953    }
954
955    public void setUserRetryCount(int retryCount) {
956        this.userRetryCount = retryCount;
957    }
958
959    public void incrmentUserRetryCount() {
960        this.userRetryCount++;
961    }
962
963    @Override
964    public int getUserRetryMax() {
965        return userRetryMax;
966    }
967
968    /**
969     * Set user retry max
970     *
971     * @param retryMax
972     */
973    public void setUserRetryMax(int retryMax) {
974        this.userRetryMax = retryMax;
975    }
976
977    @Override
978    public int getUserRetryInterval() {
979        return userRetryInterval;
980    }
981
982    public void setUserRetryInterval(int retryInterval) {
983        this.userRetryInterval = retryInterval;
984    }
985
986    @Override
987    public String getTransition() {
988        return transition;
989    }
990
991    /**
992     * Set transition
993     *
994     * @param transition
995     */
996    public void setTransition(String transition) {
997        this.transition = transition;
998    }
999
1000    @Override
1001    public String getData() {
1002        return data == null ? null : data.getString();
1003    }
1004
1005    /**
1006     * Set data
1007     *
1008     * @param data
1009     */
1010    public void setData(String data) {
1011        if (this.data == null) {
1012            this.data = new StringBlob(data);
1013        }
1014        else {
1015            this.data.setString(data);
1016        }
1017    }
1018
1019    public void setDataBlob(StringBlob data) {
1020        this.data = data;
1021    }
1022
1023    public StringBlob getDataBlob() {
1024        return data;
1025    }
1026
1027    @Override
1028    public String getStats() {
1029        return stats == null ? null : stats.getString();
1030    }
1031
1032    /**
1033     * Set stats
1034     *
1035     * @param stats
1036     */
1037    public void setStats(String stats) {
1038        if (this.stats == null) {
1039            this.stats = new StringBlob(stats);
1040        }
1041        else {
1042            this.stats.setString(stats);
1043        }
1044    }
1045
1046    public void setStatsBlob(StringBlob stats) {
1047        this.stats = stats;
1048    }
1049
1050    public StringBlob getStatsBlob() {
1051        return this.stats;
1052    }
1053
1054    @Override
1055    public String getExternalId() {
1056        return externalId;
1057    }
1058
1059    /**
1060     * Set external Id
1061     *
1062     * @param externalId
1063     */
1064    public void setExternalId(String externalId) {
1065        this.externalId = externalId;
1066    }
1067
1068    @Override
1069    public String getExternalStatus() {
1070        return externalStatus;
1071    }
1072
1073    /**
1074     * Set external status
1075     *
1076     * @param externalStatus
1077     */
1078    public void setExternalStatus(String externalStatus) {
1079        this.externalStatus = externalStatus;
1080    }
1081
1082    @Override
1083    public String getTrackerUri() {
1084        return trackerUri;
1085    }
1086
1087    /**
1088     * Set tracker uri
1089     *
1090     * @param trackerUri
1091     */
1092    public void setTrackerUri(String trackerUri) {
1093        this.trackerUri = trackerUri;
1094    }
1095
1096    @Override
1097    public String getConsoleUrl() {
1098        return consoleUrl;
1099    }
1100
1101    /**
1102     * Set console URL
1103     *
1104     * @param consoleUrl
1105     */
1106    public void setConsoleUrl(String consoleUrl) {
1107        this.consoleUrl = consoleUrl;
1108    }
1109
1110    @Override
1111    public String getErrorCode() {
1112        return errorCode;
1113    }
1114
1115    @Override
1116    public String getErrorMessage() {
1117        return errorMessage;
1118    }
1119
1120    /**
1121     * Set the error Info
1122     *
1123     * @param errorCode
1124     * @param errorMessage
1125     */
1126    public void setErrorInfo(String errorCode, String errorMessage) {
1127        this.errorCode = errorCode;
1128        if (errorMessage != null && errorMessage.length() > 500) {
1129            errorMessage = errorMessage.substring(0, 500);
1130        }
1131        this.errorMessage = errorMessage;
1132    }
1133
1134    @Override
1135    public String toString() {
1136        return MessageFormat.format("Action name[{0}] status[{1}]", getName(), getStatus());
1137    }
1138
1139    /**
1140     * Convert a nodes list into a JSONArray.
1141     *
1142     * @param nodes nodes list.
1143     * @param timeZoneId time zone to use for dates in the JSON array.
1144     * @return the corresponding JSON array.
1145     */
1146    @SuppressWarnings("unchecked")
1147    public static JSONArray toJSONArray(List<WorkflowActionBean> nodes, String timeZoneId) {
1148        JSONArray array = new JSONArray();
1149        for (WorkflowActionBean node : nodes) {
1150            array.add(node.toJSONObject(timeZoneId));
1151        }
1152        return array;
1153    }
1154
1155}