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.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.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", length = 1024)
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, getCred());
140            WritableUtils.writeStr(dataOutput, getType());
141            WritableUtils.writeStr(dataOutput, getConf());
142            WritableUtils.writeStr(dataOutput, getStatusStr());
143            dataOutput.writeInt(getRetries());
144            dataOutput.writeLong((getStartTime() != null) ? getStartTime().getTime() : -1);
145            dataOutput.writeLong((getEndTime() != null) ? getEndTime().getTime() : -1);
146            dataOutput.writeLong((getLastCheckTime() != null) ? getLastCheckTime().getTime() : -1);
147            WritableUtils.writeStr(dataOutput, getTransition());
148            WritableUtils.writeStr(dataOutput, getData());
149            WritableUtils.writeStr(dataOutput, getExternalId());
150            WritableUtils.writeStr(dataOutput, getExternalStatus());
151            WritableUtils.writeStr(dataOutput, getTrackerUri());
152            WritableUtils.writeStr(dataOutput, getConsoleUrl());
153            WritableUtils.writeStr(dataOutput, getErrorCode());
154            WritableUtils.writeStr(dataOutput, getErrorMessage());
155            WritableUtils.writeStr(dataOutput, wfId);
156            WritableUtils.writeStr(dataOutput, executionPath);
157            dataOutput.writeInt(pending);
158            dataOutput.writeLong((pendingAge != null) ? pendingAge.getTime() : -1);
159            WritableUtils.writeStr(dataOutput, signalValue);
160            WritableUtils.writeStr(dataOutput, logToken);
161        }
162    
163        /**
164         * Deserialize an action bean from a data input.
165         *
166         * @param dataInput data input.
167         * @throws IOException thrown if the action bean could not be deserialized.
168         */
169        public void readFields(DataInput dataInput) throws IOException {
170            setId(WritableUtils.readStr(dataInput));
171            setName(WritableUtils.readStr(dataInput));
172            setCred(WritableUtils.readStr(dataInput));
173            setType(WritableUtils.readStr(dataInput));
174            setConf(WritableUtils.readStr(dataInput));
175            setStatus(WorkflowAction.Status.valueOf(WritableUtils.readStr(dataInput)));
176            setRetries(dataInput.readInt());
177            long d = dataInput.readLong();
178            if (d != -1) {
179                setStartTime(new Date(d));
180            }
181            d = dataInput.readLong();
182            if (d != -1) {
183                setEndTime(new Date(d));
184            }
185            d = dataInput.readLong();
186            if (d != -1) {
187                setLastCheckTime(new Date(d));
188            }
189            setTransition(WritableUtils.readStr(dataInput));
190            setData(WritableUtils.readStr(dataInput));
191            setExternalId(WritableUtils.readStr(dataInput));
192            setExternalStatus(WritableUtils.readStr(dataInput));
193            setTrackerUri(WritableUtils.readStr(dataInput));
194            setConsoleUrl(WritableUtils.readStr(dataInput));
195            setErrorInfo(WritableUtils.readStr(dataInput), WritableUtils.readStr(dataInput));
196            wfId = WritableUtils.readStr(dataInput);
197            executionPath = WritableUtils.readStr(dataInput);
198            pending = dataInput.readInt();
199            d = dataInput.readLong();
200            if (d != -1) {
201                pendingAge = new Date(d);
202                pendingAgeTimestamp = DateUtils.convertDateToTimestamp(pendingAge);
203            }
204            signalValue = WritableUtils.readStr(dataInput);
205            logToken = WritableUtils.readStr(dataInput);
206        }
207    
208        /**
209         * Return if the action execution is complete.
210         *
211         * @return if the action start is complete.
212         */
213        public boolean isExecutionComplete() {
214            return getStatus() == WorkflowAction.Status.DONE;
215        }
216    
217        /**
218         * Return if the action is START_RETRY or START_MANUAL or END_RETRY or
219         * END_MANUAL.
220         *
221         * @return boolean true if status is START_RETRY or START_MANUAL or END_RETRY or
222         *         END_MANUAL
223         */
224        public boolean isRetryOrManual() {
225            return (getStatus() == WorkflowAction.Status.START_RETRY || getStatus() == WorkflowAction.Status.START_MANUAL
226                    || getStatus() == WorkflowAction.Status.END_RETRY || getStatus() == WorkflowAction.Status.END_MANUAL);
227        }
228    
229        /**
230         * Return if the action is complete.
231         *
232         * @return if the action is complete.
233         */
234        public boolean isComplete() {
235            return getStatus() == WorkflowAction.Status.OK || getStatus() == WorkflowAction.Status.KILLED ||
236                    getStatus() == WorkflowAction.Status.ERROR;
237        }
238    
239        /**
240         * Set the action pending flag to true.
241         */
242        public void setPendingOnly() {
243            pending = 1;
244        }
245    
246        /**
247         * Set the action as pending and the current time as pending.
248         */
249        public void setPending() {
250            pending = 1;
251            pendingAge = new Date();
252            pendingAgeTimestamp = DateUtils.convertDateToTimestamp(pendingAge);
253        }
254    
255        /**
256         * Set a time when the action will be pending, normally a time in the future.
257         *
258         * @param pendingAge the time when the action will be pending.
259         */
260        public void setPendingAge(Date pendingAge) {
261            this.pendingAge = pendingAge;
262            this.pendingAgeTimestamp = DateUtils.convertDateToTimestamp(pendingAge);
263        }
264    
265        /**
266         * Return the pending age of the action.
267         *
268         * @return the pending age of the action, <code>null</code> if the action is not pending.
269         */
270        public Date getPendingAge() {
271            return DateUtils.toDate(pendingAgeTimestamp);
272        }
273    
274        /**
275         * Return if the action is pending.
276         *
277         * @return if the action is pending.
278         */
279        public boolean isPending() {
280            return pending == 1 ? true : false;
281        }
282    
283        /**
284         * Removes the pending flag and pendingAge from the action.
285         */
286        public void resetPending() {
287            pending = 0;
288            pendingAge = null;
289            pendingAgeTimestamp = null;
290        }
291    
292        /**
293         * Removes the pending flag from the action.
294         */
295        public void resetPendingOnly() {
296            pending = 0;
297        }
298    
299        /**
300         * Increments the number of retries for the action.
301         */
302        public void incRetries() {
303            setRetries(getRetries() + 1);
304        }
305    
306        /**
307         * Set a tracking information for an action, and set the action status to {@link Action.Status#DONE}
308         *
309         * @param externalId external ID for the action.
310         * @param trackerUri tracker URI for the action.
311         * @param consoleUrl console URL for the action.
312         */
313        public void setStartData(String externalId, String trackerUri, String consoleUrl) {
314            setExternalId(ParamChecker.notEmpty(externalId, "externalId"));
315            setTrackerUri(ParamChecker.notEmpty(trackerUri, "trackerUri"));
316            setConsoleUrl(ParamChecker.notEmpty(consoleUrl, "consoleUrl"));
317            Date now = new Date();
318            setStartTime(now);
319            setLastCheckTime(now);
320            setStatus(Status.RUNNING);
321        }
322    
323        /**
324         * Set the completion information for an action start. Sets the Action status to {@link Action.Status#DONE}
325         *
326         * @param externalStatus action external end status.
327         * @param actionData action output data, <code>null</code> if there is no action output data.
328         */
329        public void setExecutionData(String externalStatus, Properties actionData) {
330            setStatus(Status.DONE);
331            setExternalStatus(ParamChecker.notEmpty(externalStatus, "externalStatus"));
332            if (actionData != null) {
333                setData(PropertiesUtils.propertiesToString(actionData));
334            }
335        }
336    
337        /**
338         * Set the completion information for an action end.
339         *
340         * @param status action status, {@link Action.Status#OK} or {@link Action.Status#ERROR} or {@link
341         * Action.Status#KILLED}
342         * @param signalValue the signal value. In most cases, the value should be OK or ERROR.
343         */
344        public void setEndData(Status status, String signalValue) {
345            if (status == null || (status != Status.OK && status != Status.ERROR && status != Status.KILLED)) {
346                throw new IllegalArgumentException("Action status must be OK, ERROR or KILLED. Received ["
347                        + status.toString() + "]");
348            }
349            if (status == Status.OK) {
350                setErrorInfo(null, null);
351            }
352            setStatus(status);
353            setSignalValue(ParamChecker.notEmpty(signalValue, "signalValue"));
354        }
355    
356    
357        /**
358         * Return the job Id.
359         *
360         * @return the job Id.
361         */
362        public String getJobId() {
363            return wfId;
364        }
365    
366        /**
367         * Return the job Id.
368         *
369         * @return the job Id.
370         */
371        public String getWfId() {
372            return wfId;
373        }
374    
375        /**
376         * Set the job id.
377         *
378         * @param id jobId;
379         */
380        public void setJobId(String id) {
381            this.wfId = id;
382        }
383    
384        public String getSlaXml() {
385            return slaXml;
386        }
387    
388        public void setSlaXml(String slaXml) {
389            this.slaXml = slaXml;
390        }
391    
392        @Override
393        public void setStatus(Status val) {
394            this.status = val.toString();
395            super.setStatus(val);
396        }
397    
398        public String getStatusStr() {
399            return status;
400        }
401    
402        @Override
403        public Status getStatus() {
404            return Status.valueOf(this.status);
405        }
406    
407        /**
408         * Return the node execution path.
409         *
410         * @return the node execution path.
411         */
412        public String getExecutionPath() {
413            return executionPath;
414        }
415    
416        /**
417         * Set the node execution path.
418         *
419         * @param executionPath the node execution path.
420         */
421        public void setExecutionPath(String executionPath) {
422            this.executionPath = executionPath;
423        }
424    
425        /**
426         * Return the signal value for the action. <p/> For decision nodes it is the choosen transition, for actions it is
427         * OK or ERROR.
428         *
429         * @return the action signal value.
430         */
431        public String getSignalValue() {
432            return signalValue;
433        }
434    
435        /**
436         * Set the signal value for the action. <p/> For decision nodes it is the choosen transition, for actions it is OK
437         * or ERROR.
438         *
439         * @param signalValue the action signal value.
440         */
441        public void setSignalValue(String signalValue) {
442            this.signalValue = signalValue;
443        }
444    
445        /**
446         * Return the job log token.
447         *
448         * @return the job log token.
449         */
450        public String getLogToken() {
451            return logToken;
452        }
453    
454        /**
455         * Set the job log token.
456         *
457         * @param logToken the job log token.
458         */
459        public void setLogToken(String logToken) {
460            this.logToken = logToken;
461        }
462    
463        /**
464         * Return the action last check time
465         *
466         * @return the last check time
467         */
468        public Date getLastCheckTime() {
469            return DateUtils.toDate(lastCheckTimestamp);
470        }
471    
472        /**
473         * Return the action last check time
474         *
475         * @return the last check time
476         */
477        public Timestamp getLastCheckTimestamp() {
478            return lastCheckTimestamp;
479        }
480    
481        /**
482         * Return the action last check time
483         *
484         * @return the last check time
485         */
486        public Timestamp getStartTimestamp() {
487            return startTimestamp;
488        }
489    
490        /**
491         * Return the action last check time
492         *
493         * @return the last check time
494         */
495        public Timestamp getEndTimestamp() {
496            return endTimestamp;
497        }
498    
499    
500        /**
501         * Return the action last check time
502         *
503         * @return the last check time
504         */
505        public Timestamp getPendingAgeTimestamp() {
506            return pendingAgeTimestamp;
507        }
508    
509        /**
510         * Sets the action last check time
511         *
512         * @param lastCheckTime the last check time to set.
513         */
514        public void setLastCheckTime(Date lastCheckTime) {
515            this.lastCheckTimestamp = DateUtils.convertDateToTimestamp(lastCheckTime);
516        }
517    
518        public boolean getPending() {
519            return this.pending == 1 ? true : false;
520        }
521    
522        @Override
523        public Date getStartTime() {
524            return DateUtils.toDate(startTimestamp);
525        }
526    
527        @Override
528        public void setStartTime(Date startTime) {
529            super.setStartTime(startTime);
530            this.startTimestamp = DateUtils.convertDateToTimestamp(startTime);
531        }
532    
533        @Override
534        public Date getEndTime() {
535            return DateUtils.toDate(endTimestamp);
536        }
537    
538        @Override
539        public void setEndTime(Date endTime) {
540            super.setEndTime(endTime);
541            this.endTimestamp = DateUtils.convertDateToTimestamp(endTime);
542        }
543    
544    }