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