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, a.statusStr = :status 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 isTerminalState Return whether workflow action in terminal state or not
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     * @param i the flag
439     */
440    public void setPending(int i) {
441        pending = i;
442    }
443
444    /**
445     * Set a time when the action will be pending, normally a time in the
446     * future.
447     *
448     * @param pendingAge the time when the action will be pending.
449     */
450    public void setPendingAge(Date pendingAge) {
451        this.pendingAgeTimestamp = DateUtils.convertDateToTimestamp(pendingAge);
452    }
453
454    /**
455     * Return the pending age of the action.
456     *
457     * @return the pending age of the action, <code>null</code> if the action is
458     *         not pending.
459     */
460    public Date getPendingAge() {
461        return DateUtils.toDate(pendingAgeTimestamp);
462    }
463
464    /**
465     * Return if the action is pending.
466     *
467     * @return if the action is pending.
468     */
469    public boolean isPending() {
470        return pending == 1 ? true : false;
471    }
472
473    /**
474     * Removes the pending flag and pendingAge from the action.
475     */
476    public void resetPending() {
477        pending = 0;
478        pendingAgeTimestamp = null;
479    }
480
481    /**
482     * Removes the pending flag from the action.
483     */
484    public void resetPendingOnly() {
485        pending = 0;
486    }
487
488    /**
489     * Increments the number of retries for the action.
490     */
491    public void incRetries() {
492        setRetries(getRetries() + 1);
493    }
494
495    /**
496     * Set a tracking information for an action, and set the action status to
497     * {@link org.apache.oozie.client.WorkflowAction.Status#DONE}
498     *
499     * @param externalId external ID for the action.
500     * @param trackerUri tracker URI for the action.
501     * @param consoleUrl console URL for the action.
502     */
503    public void setStartData(String externalId, String trackerUri, String consoleUrl) {
504        setExternalId(ParamChecker.notEmpty(externalId, "externalId"));
505        setTrackerUri(ParamChecker.notEmpty(trackerUri, "trackerUri"));
506        setConsoleUrl(ParamChecker.notEmpty(consoleUrl, "consoleUrl"));
507        Date now = new Date();
508        if (this.startTimestamp == null) {
509            setStartTime(now);
510        }
511        setLastCheckTime(now);
512        setStatus(Status.RUNNING);
513    }
514
515    /**
516     * Set the completion information for an action start. Sets the Action
517     * status to {@link org.apache.oozie.client.WorkflowAction.Status#DONE}
518     *
519     * @param externalStatus action external end status.
520     * @param actionData action output data, <code>null</code> if there is no
521     *        action output data.
522     */
523    public void setExecutionData(String externalStatus, Properties actionData) {
524        setStatus(Status.DONE);
525        setExternalStatus(ParamChecker.notEmpty(externalStatus, "externalStatus"));
526        if (actionData != null) {
527            setData(PropertiesUtils.propertiesToString(actionData));
528        }
529    }
530
531    /**
532     * Return the action statistics info.
533     *
534     * @return Json representation of the stats.
535     */
536    public String getExecutionStats() {
537        return getStats();
538    }
539
540    /**
541     * Set the action statistics info for the workflow action.
542     *
543     * @param jsonStats representation of the stats.
544     */
545    public void setExecutionStats(String jsonStats) {
546        setStats(jsonStats);
547    }
548
549    /**
550     * Return the external child IDs.
551     *
552     * @return externalChildIDs as a string.
553     */
554    @Override
555    public String getExternalChildIDs() {
556        return externalChildIDs == null ? null : externalChildIDs.getString();
557    }
558
559    /**
560     * Set the external child IDs for the workflow action.
561     *
562     * @param externalChildIDs as a string.
563     */
564    public void setExternalChildIDs(String externalChildIDs) {
565        if (this.externalChildIDs == null) {
566            this.externalChildIDs = new StringBlob(externalChildIDs);
567        }
568        else {
569            this.externalChildIDs.setString(externalChildIDs);
570        }
571    }
572
573    /**
574     * Set external child ids
575     *
576     * @param externalChildIDs the external child ids
577     */
578    public void setExternalChildIDsBlob(StringBlob externalChildIDs) {
579        this.externalChildIDs = externalChildIDs;
580    }
581
582    /**
583     * Get external ChildIds
584     *
585     * @return externalChildIDs Get external ChildIds
586     */
587    public StringBlob getExternalChildIDsBlob() {
588        return externalChildIDs;
589    }
590
591    /**
592     * Set the completion information for an action end.
593     *
594     * @param status action status, {@link org.apache.oozie.client.WorkflowAction.Status#OK} or
595     *        {@link org.apache.oozie.client.WorkflowAction.Status#ERROR} or
596     *        {@link org.apache.oozie.client.WorkflowAction.Status#KILLED}
597     * @param signalValue the signal value. In most cases, the value should be
598     *        OK or ERROR.
599     */
600    public void setEndData(Status status, String signalValue) {
601        if (status == null || (status != Status.OK && status != Status.ERROR && status != Status.KILLED)) {
602            throw new IllegalArgumentException("Action status must be OK, ERROR or KILLED. Received ["
603                    + (status == null ? "null" : status.toString()) + "]");
604        }
605        if (status == Status.OK) {
606            setErrorInfo(null, null);
607        }
608        setStatus(status);
609        setSignalValue(ParamChecker.notEmpty(signalValue, "signalValue"));
610    }
611
612    /**
613     * Return the job Id.
614     *
615     * @return the job Id.
616     */
617    public String getJobId() {
618        return wfId;
619    }
620
621    /**
622     * Return the job Id.
623     *
624     * @return the job Id.
625     */
626    public String getWfId() {
627        return wfId;
628    }
629
630    /**
631     * Set the job id.
632     *
633     * @param id jobId;
634     */
635    public void setJobId(String id) {
636        this.wfId = id;
637    }
638
639    public void setSlaXml(String slaXmlStr) {
640        if (this.slaXml == null) {
641            this.slaXml = new StringBlob(slaXmlStr);
642        }
643        else {
644            this.slaXml.setString(slaXmlStr);
645        }
646    }
647
648    public String getSlaXml() {
649        return slaXml == null ? null : slaXml.getString();
650    }
651
652    public void setSlaXmlBlob(StringBlob slaXml) {
653        this.slaXml = slaXml;
654    }
655
656    public StringBlob getSlaXmlBlob() {
657        return slaXml;
658    }
659
660    /**
661     * Set status of job
662     *
663     * @param val the status
664     */
665    public void setStatus(Status val) {
666        this.statusStr = val.toString();
667    }
668
669    @Override
670    public Status getStatus() {
671        return Status.valueOf(this.statusStr);
672    }
673
674    /**
675     * Set status
676     *
677     * @param statusStr the status
678     */
679    public void setStatusStr(String statusStr) {
680        this.statusStr = statusStr;
681    }
682
683    /**
684     * Get status
685     *
686     * @return statusStr Get status
687     */
688    public String getStatusStr() {
689        return statusStr;
690    }
691
692    /**
693     * Return the node execution path.
694     *
695     * @return the node execution path.
696     */
697    public String getExecutionPath() {
698        return executionPath;
699    }
700
701    /**
702     * Set the node execution path.
703     *
704     * @param executionPath the node execution path.
705     */
706    public void setExecutionPath(String executionPath) {
707        this.executionPath = executionPath;
708    }
709
710    /**
711     * Return the signal value for the action.
712     * <p>
713     * For decision nodes it is the choosen transition, for actions it is OK or
714     * ERROR.
715     *
716     * @return the action signal value.
717     */
718    public String getSignalValue() {
719        return signalValue;
720    }
721
722    /**
723     * Set the signal value for the action.
724     * <p>
725     * For decision nodes it is the choosen transition, for actions it is OK or
726     * ERROR.
727     *
728     * @param signalValue the action signal value.
729     */
730    public void setSignalValue(String signalValue) {
731        this.signalValue = signalValue;
732    }
733
734    /**
735     * Return the job log token.
736     *
737     * @return the job log token.
738     */
739    public String getLogToken() {
740        return logToken;
741    }
742
743    /**
744     * Set the job log token.
745     *
746     * @param logToken the job log token.
747     */
748    public void setLogToken(String logToken) {
749        this.logToken = logToken;
750    }
751
752    /**
753     * Return the action last check time
754     *
755     * @return the last check time
756     */
757    public Date getLastCheckTime() {
758        return DateUtils.toDate(lastCheckTimestamp);
759    }
760
761    /**
762     * Return the action last check time
763     *
764     * @return the last check time
765     */
766    public Timestamp getLastCheckTimestamp() {
767        return lastCheckTimestamp;
768    }
769
770    /**
771     * Return the action last check time
772     *
773     * @return the last check time
774     */
775    public Timestamp getStartTimestamp() {
776        return startTimestamp;
777    }
778
779    /**
780     * Return the action last check time
781     *
782     * @return the last check time
783     */
784    public Timestamp getEndTimestamp() {
785        return endTimestamp;
786    }
787
788    /**
789     * Return the action last check time
790     *
791     * @return the last check time
792     */
793    public Timestamp getPendingAgeTimestamp() {
794        return pendingAgeTimestamp;
795    }
796
797    /**
798     * Sets the action last check time
799     *
800     * @param lastCheckTime the last check time to set.
801     */
802    public void setLastCheckTime(Date lastCheckTime) {
803        this.lastCheckTimestamp = DateUtils.convertDateToTimestamp(lastCheckTime);
804    }
805
806    public int getPending() {
807        return this.pending;
808    }
809
810    @Override
811    public Date getStartTime() {
812        return DateUtils.toDate(startTimestamp);
813    }
814
815    /**
816     * Set start time
817     *
818     * @param startTime the start time
819     */
820    public void setStartTime(Date startTime) {
821        this.startTimestamp = DateUtils.convertDateToTimestamp(startTime);
822    }
823
824    @Override
825    public Date getEndTime() {
826        return DateUtils.toDate(endTimestamp);
827    }
828
829    /**
830     * Set end time
831     *
832     * @param endTime the end time
833     */
834    public void setEndTime(Date endTime) {
835        this.endTimestamp = DateUtils.convertDateToTimestamp(endTime);
836    }
837
838    @SuppressWarnings("unchecked")
839    public JSONObject toJSONObject() {
840        return toJSONObject("GMT");
841    }
842
843    @SuppressWarnings("unchecked")
844    public JSONObject toJSONObject(String timeZoneId) {
845        JSONObject json = new JSONObject();
846        json.put(JsonTags.WORKFLOW_ACTION_ID, id);
847        json.put(JsonTags.WORKFLOW_ACTION_NAME, name);
848        json.put(JsonTags.WORKFLOW_ACTION_AUTH, cred);
849        json.put(JsonTags.WORKFLOW_ACTION_TYPE, type);
850        json.put(JsonTags.WORKFLOW_ACTION_CONF, getConf());
851        json.put(JsonTags.WORKFLOW_ACTION_STATUS, statusStr);
852        json.put(JsonTags.WORKFLOW_ACTION_RETRIES, (long) retries);
853        json.put(JsonTags.WORKFLOW_ACTION_START_TIME, JsonUtils.formatDateRfc822(getStartTime(), timeZoneId));
854        json.put(JsonTags.WORKFLOW_ACTION_END_TIME, JsonUtils.formatDateRfc822(getEndTime(), timeZoneId));
855        json.put(JsonTags.WORKFLOW_ACTION_TRANSITION, transition);
856        json.put(JsonTags.WORKFLOW_ACTION_DATA, getData());
857        json.put(JsonTags.WORKFLOW_ACTION_STATS, getStats());
858        json.put(JsonTags.WORKFLOW_ACTION_EXTERNAL_CHILD_IDS, getExternalChildIDs());
859        json.put(JsonTags.WORKFLOW_ACTION_EXTERNAL_ID, externalId);
860        json.put(JsonTags.WORKFLOW_ACTION_EXTERNAL_STATUS, externalStatus);
861        json.put(JsonTags.WORKFLOW_ACTION_TRACKER_URI, trackerUri);
862        json.put(JsonTags.WORKFLOW_ACTION_CONSOLE_URL, consoleUrl);
863        json.put(JsonTags.WORKFLOW_ACTION_ERROR_CODE, errorCode);
864        json.put(JsonTags.WORKFLOW_ACTION_ERROR_MESSAGE, errorMessage);
865        json.put(JsonTags.TO_STRING, toString());
866        json.put(JsonTags.WORKFLOW_ACTION_USER_RETRY_INTERVAL, userRetryInterval);
867        json.put(JsonTags.WORKFLOW_ACTION_USER_RETRY_COUNT, userRetryCount);
868        json.put(JsonTags.WORKFLOW_ACTION_USER_RETRY_MAX, userRetryMax);
869        json.put(JsonTags.WORKFLOW_ACTION_CRED, cred);
870        return json;
871    }
872
873    @Override
874    public String getId() {
875        return id;
876    }
877
878    public void setId(String id) {
879        this.id = id;
880    }
881
882    public Timestamp getCreatedTimestamp() {
883        return createdTimeTS;
884    }
885
886    public Date getCreatedTime() {
887        return DateUtils.toDate(createdTimeTS);
888    }
889
890    public void setCreatedTime(Date createdTime) {
891        this.createdTimeTS = DateUtils.convertDateToTimestamp(createdTime);
892    }
893
894    @Override
895    public String getName() {
896        return name;
897    }
898
899    public void setName(String name) {
900        this.name = name;
901    }
902
903    @Override
904    public String getCred() {
905        return cred;
906    }
907
908    public void setCred(String cred) {
909        this.cred = cred;
910    }
911
912    @Override
913    public String getType() {
914        return type;
915    }
916
917    public void setType(String type) {
918        this.type = type;
919    }
920
921    @Override
922    public String getConf() {
923        return conf == null ? null : conf.getString();
924    }
925
926    public void setConf(String conf) {
927        if (this.conf == null) {
928            this.conf = new StringBlob(conf);
929        }
930        else {
931            this.conf.setString(conf);
932        }
933    }
934
935    public void setConfBlob(StringBlob conf) {
936        this.conf = conf;
937    }
938
939    public StringBlob getConfBlob() {
940        return conf;
941    }
942
943    @Override
944    public int getRetries() {
945        return retries;
946    }
947
948    public void setRetries(int retries) {
949        this.retries = retries;
950    }
951
952    @Override
953    public int getUserRetryCount() {
954        return userRetryCount;
955    }
956
957    public void setUserRetryCount(int retryCount) {
958        this.userRetryCount = retryCount;
959    }
960
961    public void incrmentUserRetryCount() {
962        this.userRetryCount++;
963    }
964
965    @Override
966    public int getUserRetryMax() {
967        return userRetryMax;
968    }
969
970    /**
971     * Set user retry max
972     *
973     * @param retryMax the maximum retry count
974     */
975    public void setUserRetryMax(int retryMax) {
976        this.userRetryMax = retryMax;
977    }
978
979    @Override
980    public int getUserRetryInterval() {
981        return userRetryInterval;
982    }
983
984    public void setUserRetryInterval(int retryInterval) {
985        this.userRetryInterval = retryInterval;
986    }
987
988    @Override
989    public String getTransition() {
990        return transition;
991    }
992
993    /**
994     * Set transition
995     *
996     * @param transition the transition
997     */
998    public void setTransition(String transition) {
999        this.transition = transition;
1000    }
1001
1002    @Override
1003    public String getData() {
1004        return data == null ? null : data.getString();
1005    }
1006
1007    /**
1008     * Set data
1009     *
1010     * @param data the data
1011     */
1012    public void setData(String data) {
1013        if (this.data == null) {
1014            this.data = new StringBlob(data);
1015        }
1016        else {
1017            this.data.setString(data);
1018        }
1019    }
1020
1021    public void setDataBlob(StringBlob data) {
1022        this.data = data;
1023    }
1024
1025    public StringBlob getDataBlob() {
1026        return data;
1027    }
1028
1029    @Override
1030    public String getStats() {
1031        return stats == null ? null : stats.getString();
1032    }
1033
1034    /**
1035     * Set stats
1036     *
1037     * @param stats the action stats
1038     */
1039    public void setStats(String stats) {
1040        if (this.stats == null) {
1041            this.stats = new StringBlob(stats);
1042        }
1043        else {
1044            this.stats.setString(stats);
1045        }
1046    }
1047
1048    public void setStatsBlob(StringBlob stats) {
1049        this.stats = stats;
1050    }
1051
1052    public StringBlob getStatsBlob() {
1053        return this.stats;
1054    }
1055
1056    @Override
1057    public String getExternalId() {
1058        return externalId;
1059    }
1060
1061    /**
1062     * Set external Id
1063     *
1064     * @param externalId the id
1065     */
1066    public void setExternalId(String externalId) {
1067        this.externalId = externalId;
1068    }
1069
1070    @Override
1071    public String getExternalStatus() {
1072        return externalStatus;
1073    }
1074
1075    /**
1076     * Set external status
1077     *
1078     * @param externalStatus the external status
1079     */
1080    public void setExternalStatus(String externalStatus) {
1081        this.externalStatus = externalStatus;
1082    }
1083
1084    @Override
1085    public String getTrackerUri() {
1086        return trackerUri;
1087    }
1088
1089    /**
1090     * Set tracker uri
1091     *
1092     * @param trackerUri the URI
1093     */
1094    public void setTrackerUri(String trackerUri) {
1095        this.trackerUri = trackerUri;
1096    }
1097
1098    @Override
1099    public String getConsoleUrl() {
1100        return consoleUrl;
1101    }
1102
1103    /**
1104     * Set console URL
1105     *
1106     * @param consoleUrl the URL
1107     */
1108    public void setConsoleUrl(String consoleUrl) {
1109        this.consoleUrl = consoleUrl;
1110    }
1111
1112    @Override
1113    public String getErrorCode() {
1114        return errorCode;
1115    }
1116
1117    @Override
1118    public String getErrorMessage() {
1119        return errorMessage;
1120    }
1121
1122    /**
1123     * Set the error Info
1124     *
1125     * @param errorCode the error code
1126     * @param errorMessage the error message
1127     */
1128    public void setErrorInfo(String errorCode, String errorMessage) {
1129        this.errorCode = errorCode;
1130        if (errorMessage != null && errorMessage.length() > 500) {
1131            errorMessage = errorMessage.substring(0, 500);
1132        }
1133        this.errorMessage = errorMessage;
1134    }
1135
1136    @Override
1137    public String toString() {
1138        return MessageFormat.format("Action name[{0}] status[{1}]", getName(), getStatus());
1139    }
1140
1141    /**
1142     * Convert a nodes list into a JSONArray.
1143     *
1144     * @param nodes nodes list.
1145     * @param timeZoneId time zone to use for dates in the JSON array.
1146     * @return the corresponding JSON array.
1147     */
1148    @SuppressWarnings("unchecked")
1149    public static JSONArray toJSONArray(List<WorkflowActionBean> nodes, String timeZoneId) {
1150        JSONArray array = new JSONArray();
1151        for (WorkflowActionBean node : nodes) {
1152            array.add(node.toJSONObject(timeZoneId));
1153        }
1154        return array;
1155    }
1156
1157}