001    /**
002     * Copyright (c) 2010 Yahoo! Inc. All rights reserved.
003     * Licensed under the Apache License, Version 2.0 (the "License");
004     * you may not use this file except in compliance with the License.
005     * You may obtain a copy of the License at
006     *
007     *   http://www.apache.org/licenses/LICENSE-2.0
008     *
009     *  Unless required by applicable law or agreed to in writing, software
010     *  distributed under the License is distributed on an "AS IS" BASIS,
011     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012     *  See the License for the specific language governing permissions and
013     *  limitations under the License. See accompanying LICENSE file.
014     */
015    package org.apache.oozie;
016    
017    import java.io.DataInput;
018    import java.io.DataOutput;
019    import java.io.IOException;
020    import java.sql.Timestamp;
021    import java.util.Date;
022    import java.util.Properties;
023    
024    import javax.persistence.Basic;
025    import javax.persistence.Column;
026    import javax.persistence.Entity;
027    import javax.persistence.Lob;
028    import javax.persistence.NamedQueries;
029    import javax.persistence.NamedQuery;
030    import javax.persistence.Transient;
031    
032    import org.apache.hadoop.io.Writable;
033    import org.apache.oozie.client.WorkflowAction;
034    import org.apache.oozie.client.rest.JsonWorkflowAction;
035    import org.apache.oozie.util.DateUtils;
036    import org.apache.oozie.util.ParamChecker;
037    import org.apache.oozie.util.PropertiesUtils;
038    import org.apache.oozie.util.WritableUtils;
039    import org.apache.openjpa.persistence.jdbc.Index;
040    
041    /**
042     * Bean that contains all the information to start an action for a workflow node.
043     */
044    @Entity
045    @NamedQueries({
046    
047        @NamedQuery(name = "UPDATE_ACTION", query = "update WorkflowActionBean a set a.conf = :conf, a.consoleUrl = :consoleUrl, a.data = :data, a.errorCode = :errorCode, a.errorMessage = :errorMessage, a.externalId = :externalId, a.externalStatus = :externalStatus, a.name = :name, 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.status = :status, a.wfId=:wfId where a.id = :id"),
048    
049        @NamedQuery(name = "DELETE_ACTION", query = "delete from WorkflowActionBean a where a.id = :id"),
050    
051        @NamedQuery(name = "DELETE_ACTIONS_FOR_WORKFLOW", query = "delete from WorkflowActionBean a where a.wfId = :wfId"),
052    
053        @NamedQuery(name = "GET_ACTIONS", query = "select OBJECT(a) from WorkflowActionBean a"),
054    
055        @NamedQuery(name = "GET_ACTION", query = "select OBJECT(a) from WorkflowActionBean a where a.id = :id"),
056    
057        @NamedQuery(name = "GET_ACTION_FOR_UPDATE", query = "select OBJECT(a) from WorkflowActionBean a where a.id = :id"),
058    
059        @NamedQuery(name = "GET_ACTIONS_FOR_WORKFLOW", query = "select OBJECT(a) from WorkflowActionBean a where a.wfId = :wfId order by a.startTimestamp"),
060    
061        @NamedQuery(name = "GET_ACTIONS_OF_WORKFLOW_FOR_UPDATE", query = "select OBJECT(a) from WorkflowActionBean a where a.wfId = :wfId order by a.startTimestamp"),
062    
063        @NamedQuery(name = "GET_PENDING_ACTIONS", query = "select OBJECT(a) from WorkflowActionBean a where a.pending = 1 AND a.pendingAgeTimestamp < :pendingAge AND a.status <> 'RUNNING'"),
064    
065        @NamedQuery(name = "GET_RUNNING_ACTIONS", query = "select OBJECT(a) from WorkflowActionBean a where a.pending = 1 AND a.status = 'RUNNING' AND a.lastCheckTimestamp < :lastCheckTime"),
066    
067        @NamedQuery(name = "GET_RETRY_MANUAL_ACTIONS", query = "select OBJECT(a) from WorkflowActionBean a where a.wfId = :wfId AND (a.status = 'START_RETRY' OR a.status = 'START_MANUAL' OR a.status = 'END_RETRY' OR a.status = 'END_MANUAL')") })
068    
069    public class WorkflowActionBean extends JsonWorkflowAction implements Writable {
070    
071        @Basic
072        @Index
073        @Column(name = "wf_id")
074        private String wfId = null;
075    
076        @Basic
077        @Index
078        @Column(name = "status")
079        private String status = WorkflowAction.Status.PREP.toString();
080    
081        @Basic
082        @Column(name = "last_check_time")
083        private java.sql.Timestamp lastCheckTimestamp;
084    
085        @Basic
086        @Column(name = "end_time")
087        private java.sql.Timestamp endTimestamp = null;
088    
089        @Basic
090        @Column(name = "start_time")
091        private java.sql.Timestamp startTimestamp = null;
092    
093        @Basic
094        @Column(name = "execution_path")
095        private String executionPath = null;
096    
097        @Basic
098        @Column(name = "pending")
099        private int pending = 0;
100    
101        // @Temporal(TemporalType.TIME)
102        // @Column(name="pending_age",columnDefinition="timestamp default '0000-00-00 00:00:00'")
103        @Basic
104        @Index
105        @Column(name = "pending_age")
106        private java.sql.Timestamp pendingAgeTimestamp = null;
107    
108        @Basic
109        @Column(name = "signal_value")
110        private String signalValue = null;
111    
112        @Basic
113        @Column(name = "log_token")
114        private String logToken = null;
115    
116        @Transient
117        private Date pendingAge;
118    
119        @Column(name = "sla_xml")
120        @Lob
121        private String slaXml = null;
122    
123        /**
124         * Default constructor.
125         */
126        public WorkflowActionBean() {
127        }
128    
129        /**
130         * Serialize the action bean to a data output.
131         *
132         * @param dataOutput data output.
133         * @throws IOException thrown if the action bean could not be serialized.
134         */
135    
136        public void write(DataOutput dataOutput) throws IOException {
137            WritableUtils.writeStr(dataOutput, getId());
138            WritableUtils.writeStr(dataOutput, getName());
139            WritableUtils.writeStr(dataOutput, getType());
140            WritableUtils.writeStr(dataOutput, getConf());
141            WritableUtils.writeStr(dataOutput, getStatusStr());
142            dataOutput.writeInt(getRetries());
143            dataOutput.writeLong((getStartTime() != null) ? getStartTime().getTime() : -1);
144            dataOutput.writeLong((getEndTime() != null) ? getEndTime().getTime() : -1);
145            dataOutput.writeLong((getLastCheckTime() != null) ? getLastCheckTime().getTime() : -1);
146            WritableUtils.writeStr(dataOutput, getTransition());
147            WritableUtils.writeStr(dataOutput, getData());
148            WritableUtils.writeStr(dataOutput, getExternalId());
149            WritableUtils.writeStr(dataOutput, getExternalStatus());
150            WritableUtils.writeStr(dataOutput, getTrackerUri());
151            WritableUtils.writeStr(dataOutput, getConsoleUrl());
152            WritableUtils.writeStr(dataOutput, getErrorCode());
153            WritableUtils.writeStr(dataOutput, getErrorMessage());
154            WritableUtils.writeStr(dataOutput, wfId);
155            WritableUtils.writeStr(dataOutput, executionPath);
156            dataOutput.writeInt(pending);
157            dataOutput.writeLong((pendingAge != null) ? pendingAge.getTime() : -1);
158            WritableUtils.writeStr(dataOutput, signalValue);
159            WritableUtils.writeStr(dataOutput, logToken);
160        }
161    
162        /**
163         * Deserialize an action bean from a data input.
164         *
165         * @param dataInput data input.
166         * @throws IOException thrown if the action bean could not be deserialized.
167         */
168        public void readFields(DataInput dataInput) throws IOException {
169            setId(WritableUtils.readStr(dataInput));
170            setName(WritableUtils.readStr(dataInput));
171            setType(WritableUtils.readStr(dataInput));
172            setConf(WritableUtils.readStr(dataInput));
173            setStatus(WorkflowAction.Status.valueOf(WritableUtils.readStr(dataInput)));
174            setRetries(dataInput.readInt());
175            long d = dataInput.readLong();
176            if (d != -1) {
177                setStartTime(new Date(d));
178            }
179            d = dataInput.readLong();
180            if (d != -1) {
181                setEndTime(new Date(d));
182            }
183            d = dataInput.readLong();
184            if (d != -1) {
185                setLastCheckTime(new Date(d));
186            }
187            setTransition(WritableUtils.readStr(dataInput));
188            setData(WritableUtils.readStr(dataInput));
189            setExternalId(WritableUtils.readStr(dataInput));
190            setExternalStatus(WritableUtils.readStr(dataInput));
191            setTrackerUri(WritableUtils.readStr(dataInput));
192            setConsoleUrl(WritableUtils.readStr(dataInput));
193            setErrorInfo(WritableUtils.readStr(dataInput), WritableUtils.readStr(dataInput));
194            wfId = WritableUtils.readStr(dataInput);
195            executionPath = WritableUtils.readStr(dataInput);
196            pending = dataInput.readInt();
197            d = dataInput.readLong();
198            if (d != -1) {
199                pendingAge = new Date(d);
200                pendingAgeTimestamp = DateUtils.convertDateToTimestamp(pendingAge);
201            }
202            signalValue = WritableUtils.readStr(dataInput);
203            logToken = WritableUtils.readStr(dataInput);
204        }
205    
206        /**
207         * Return if the action execution is complete.
208         *
209         * @return if the action start is complete.
210         */
211        public boolean isExecutionComplete() {
212            return getStatus() == WorkflowAction.Status.DONE;
213        }
214    
215        /**
216         * Return if the action is START_RETRY or START_MANUAL or END_RETRY or
217         * END_MANUAL.
218         *
219         * @return boolean true if status is START_RETRY or START_MANUAL or END_RETRY or
220         *         END_MANUAL
221         */
222        public boolean isRetryOrManual() {
223            return (getStatus() == WorkflowAction.Status.START_RETRY || getStatus() == WorkflowAction.Status.START_MANUAL
224                    || getStatus() == WorkflowAction.Status.END_RETRY || getStatus() == WorkflowAction.Status.END_MANUAL);
225        }
226    
227        /**
228         * Return if the action is complete.
229         *
230         * @return if the action is complete.
231         */
232        public boolean isComplete() {
233            return getStatus() == WorkflowAction.Status.OK || getStatus() == WorkflowAction.Status.KILLED ||
234                    getStatus() == WorkflowAction.Status.ERROR;
235        }
236    
237        /**
238         * Set the action pending flag to true.
239         */
240        public void setPendingOnly() {
241            pending = 1;
242        }
243    
244        /**
245         * Set the action as pending and the current time as pending.
246         */
247        public void setPending() {
248            pending = 1;
249            pendingAge = new Date();
250            pendingAgeTimestamp = DateUtils.convertDateToTimestamp(pendingAge);
251        }
252    
253        /**
254         * Set a time when the action will be pending, normally a time in the future.
255         *
256         * @param pendingAge the time when the action will be pending.
257         */
258        public void setPendingAge(Date pendingAge) {
259            this.pendingAge = pendingAge;
260            this.pendingAgeTimestamp = DateUtils.convertDateToTimestamp(pendingAge);
261        }
262    
263        /**
264         * Return the pending age of the action.
265         *
266         * @return the pending age of the action, <code>null</code> if the action is not pending.
267         */
268        public Date getPendingAge() {
269            return DateUtils.toDate(pendingAgeTimestamp);
270        }
271    
272        /**
273         * Return if the action is pending.
274         *
275         * @return if the action is pending.
276         */
277        public boolean isPending() {
278            return pending == 1 ? true : false;
279        }
280    
281        /**
282         * Removes the pending flag and pendingAge from the action.
283         */
284        public void resetPending() {
285            pending = 0;
286            pendingAge = null;
287            pendingAgeTimestamp = null;
288        }
289    
290        /**
291         * Removes the pending flag from the action.
292         */
293        public void resetPendingOnly() {
294            pending = 0;
295        }
296    
297        /**
298         * Increments the number of retries for the action.
299         */
300        public void incRetries() {
301            setRetries(getRetries() + 1);
302        }
303    
304        /**
305         * Set a tracking information for an action, and set the action status to {@link Action.Status#DONE}
306         *
307         * @param externalId external ID for the action.
308         * @param trackerUri tracker URI for the action.
309         * @param consoleUrl console URL for the action.
310         */
311        public void setStartData(String externalId, String trackerUri, String consoleUrl) {
312            setExternalId(ParamChecker.notEmpty(externalId, "externalId"));
313            setTrackerUri(ParamChecker.notEmpty(trackerUri, "trackerUri"));
314            setConsoleUrl(ParamChecker.notEmpty(consoleUrl, "consoleUrl"));
315            Date now = new Date();
316            setStartTime(now);
317            setLastCheckTime(now);
318            setStatus(Status.RUNNING);
319        }
320    
321        /**
322         * Set the completion information for an action start. Sets the Action status to {@link Action.Status#DONE}
323         *
324         * @param externalStatus action external end status.
325         * @param actionData action output data, <code>null</code> if there is no action output data.
326         */
327        public void setExecutionData(String externalStatus, Properties actionData) {
328            setStatus(Status.DONE);
329            setExternalStatus(ParamChecker.notEmpty(externalStatus, "externalStatus"));
330            if (actionData != null) {
331                setData(PropertiesUtils.propertiesToString(actionData));
332            }
333        }
334    
335        /**
336         * Set the completion information for an action end.
337         *
338         * @param status action status, {@link Action.Status#OK} or {@link Action.Status#ERROR} or {@link
339         * Action.Status#KILLED}
340         * @param signalValue the signal value. In most cases, the value should be OK or ERROR.
341         */
342        public void setEndData(Status status, String signalValue) {
343            if (status == null || (status != Status.OK && status != Status.ERROR && status != Status.KILLED)) {
344                throw new IllegalArgumentException("Action status must be OK, ERROR or KILLED. Received ["
345                        + status.toString() + "]");
346            }
347            if (status == Status.OK) {
348                setErrorInfo(null, null);
349            }
350            setStatus(status);
351            setSignalValue(ParamChecker.notEmpty(signalValue, "signalValue"));
352        }
353    
354    
355        /**
356         * Return the job Id.
357         *
358         * @return the job Id.
359         */
360        public String getJobId() {
361            return wfId;
362        }
363    
364        /**
365         * Return the job Id.
366         *
367         * @return the job Id.
368         */
369        public String getWfId() {
370            return wfId;
371        }
372    
373        /**
374         * Set the job id.
375         *
376         * @param id jobId;
377         */
378        public void setJobId(String id) {
379            this.wfId = id;
380        }
381    
382        public String getSlaXml() {
383            return slaXml;
384        }
385    
386        public void setSlaXml(String slaXml) {
387            this.slaXml = slaXml;
388        }
389    
390        @Override
391        public void setStatus(Status val) {
392            this.status = val.toString();
393            super.setStatus(val);
394        }
395    
396        public String getStatusStr() {
397            return status;
398        }
399    
400        @Override
401        public Status getStatus() {
402            return Status.valueOf(this.status);
403        }
404    
405        /**
406         * Return the node execution path.
407         *
408         * @return the node execution path.
409         */
410        public String getExecutionPath() {
411            return executionPath;
412        }
413    
414        /**
415         * Set the node execution path.
416         *
417         * @param executionPath the node execution path.
418         */
419        public void setExecutionPath(String executionPath) {
420            this.executionPath = executionPath;
421        }
422    
423        /**
424         * Return the signal value for the action. <p/> For decision nodes it is the choosen transition, for actions it is
425         * OK or ERROR.
426         *
427         * @return the action signal value.
428         */
429        public String getSignalValue() {
430            return signalValue;
431        }
432    
433        /**
434         * Set the signal value for the action. <p/> For decision nodes it is the choosen transition, for actions it is OK
435         * or ERROR.
436         *
437         * @param signalValue the action signal value.
438         */
439        public void setSignalValue(String signalValue) {
440            this.signalValue = signalValue;
441        }
442    
443        /**
444         * Return the job log token.
445         *
446         * @return the job log token.
447         */
448        public String getLogToken() {
449            return logToken;
450        }
451    
452        /**
453         * Set the job log token.
454         *
455         * @param logToken the job log token.
456         */
457        public void setLogToken(String logToken) {
458            this.logToken = logToken;
459        }
460    
461        /**
462         * Return the action last check time
463         *
464         * @return the last check time
465         */
466        public Date getLastCheckTime() {
467            return DateUtils.toDate(lastCheckTimestamp);
468        }
469    
470        /**
471         * Return the action last check time
472         *
473         * @return the last check time
474         */
475        public Timestamp getLastCheckTimestamp() {
476            return lastCheckTimestamp;
477        }
478    
479        /**
480         * Return the action last check time
481         *
482         * @return the last check time
483         */
484        public Timestamp getStartTimestamp() {
485            return startTimestamp;
486        }
487    
488        /**
489         * Return the action last check time
490         *
491         * @return the last check time
492         */
493        public Timestamp getEndTimestamp() {
494            return endTimestamp;
495        }
496    
497    
498        /**
499         * Return the action last check time
500         *
501         * @return the last check time
502         */
503        public Timestamp getPendingAgeTimestamp() {
504            return pendingAgeTimestamp;
505        }
506    
507        /**
508         * Sets the action last check time
509         *
510         * @param lastCheckTime the last check time to set.
511         */
512        public void setLastCheckTime(Date lastCheckTime) {
513            this.lastCheckTimestamp = DateUtils.convertDateToTimestamp(lastCheckTime);
514        }
515    
516        public boolean getPending() {
517            return this.pending == 1 ? true : false;
518        }
519    
520        @Override
521        public Date getStartTime() {
522            return DateUtils.toDate(startTimestamp);
523        }
524    
525        @Override
526        public void setStartTime(Date startTime) {
527            super.setStartTime(startTime);
528            this.startTimestamp = DateUtils.convertDateToTimestamp(startTime);
529        }
530    
531        @Override
532        public Date getEndTime() {
533            return DateUtils.toDate(endTimestamp);
534        }
535    
536        @Override
537        public void setEndTime(Date endTime) {
538            super.setEndTime(endTime);
539            this.endTimestamp = DateUtils.convertDateToTimestamp(endTime);
540        }
541    
542    }