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.sla;
020
021import java.sql.Timestamp;
022import java.util.Date;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026
027import javax.persistence.Basic;
028import javax.persistence.Column;
029import javax.persistence.Entity;
030import javax.persistence.Id;
031import javax.persistence.NamedQueries;
032import javax.persistence.NamedQuery;
033import javax.persistence.Table;
034
035import org.apache.oozie.AppType;
036import org.apache.oozie.client.OozieClient;
037import org.apache.oozie.client.event.SLAEvent;
038import org.apache.oozie.client.event.SLAEvent.EventStatus;
039import org.apache.oozie.client.rest.JsonBean;
040import org.apache.oozie.client.rest.JsonTags;
041import org.apache.oozie.client.rest.JsonUtils;
042import org.apache.oozie.util.DateUtils;
043import org.apache.openjpa.persistence.jdbc.Index;
044import org.json.simple.JSONArray;
045import org.json.simple.JSONObject;
046
047@Entity
048@Table(name = "SLA_SUMMARY")
049@NamedQueries({
050
051 @NamedQuery(name = "UPDATE_SLA_SUMMARY_FOR_SLA_STATUS", query = "update  SLASummaryBean w set w.slaStatus = :slaStatus,"
052         + " w.eventStatus = :eventStatus, w.eventProcessed = :eventProcessed, w.lastModifiedTS = :lastModifiedTS "
053         + "where w.jobId = :jobId"),
054
055 @NamedQuery(name = "UPDATE_SLA_SUMMARY_FOR_STATUS_ACTUAL_TIMES", query = "update SLASummaryBean w set w.slaStatus = :slaStatus,"
056         + " w.eventStatus = :eventStatus, w.eventProcessed = :eventProcessed, w.jobStatus = :jobStatus, w.lastModifiedTS "
057         + "= :lastModifiedTS, w.actualStartTS = :actualStartTS, w.actualEndTS = :actualEndTS, w.actualDuration = :actualDuration"
058         + " where w.jobId = :jobId"),
059
060 @NamedQuery(name = "UPDATE_SLA_SUMMARY_FOR_EXPECTED_TIMES", query = "update SLASummaryBean w set w.nominalTimeTS "
061         + "= :nominalTime, w.expectedStartTS = :expectedStartTime, w.expectedEndTS = :expectedEndTime, w.expectedDuration "
062         + "= :expectedDuration , w.lastModifiedTS = :lastModTime where w.jobId = :jobId"),
063
064 @NamedQuery(name = "UPDATE_SLA_SUMMARY_EVENTPROCESSED", query = "update SLASummaryBean w set w.eventProcessed "
065         + "= :eventProcessed where w.jobId = :jobId"),
066
067 @NamedQuery(name = "UPDATE_SLA_SUMMARY_LAST_MODIFIED_TIME", query = "update SLASummaryBean w set w.lastModifiedTS "
068         + "= :lastModifiedTS where w.jobId = :jobId"),
069
070 @NamedQuery(name = "UPDATE_SLA_SUMMARY_ALL", query = "update SLASummaryBean w set w.jobId = :jobId, w.appName = :appName,"
071         + " w.appType = :appType, w.nominalTimeTS = :nominalTime, w.expectedStartTS = :expectedStartTime, w.expectedEndTS "
072         + "= :expectedEndTime, w.expectedDuration = :expectedDuration, w.jobStatus = :jobStatus, w.slaStatus = :slaStatus,"
073         + " w.eventStatus = :eventStatus, w.lastModifiedTS = :lastModTime, w.user = :user, w.parentId = :parentId,"
074         + " w.eventProcessed = :eventProcessed, w.actualDuration = :actualDuration, w.actualEndTS = :actualEndTS,"
075         + " w.actualStartTS = :actualStartTS where w.jobId = :jobId"),
076
077 @NamedQuery(name = "GET_SLA_SUMMARY", query = "select OBJECT(w) from SLASummaryBean w where w.jobId = :id"),
078
079 @NamedQuery(name = "GET_SLA_SUMMARY_RECORDS_RESTART", query = "select OBJECT(w) from SLASummaryBean w where w.eventProcessed "
080         + "<= 7 AND w.lastModifiedTS >= :lastModifiedTime"),
081
082 @NamedQuery(name = "GET_SLA_SUMMARY_EVENTPROCESSED", query = "select w.eventProcessed from SLASummaryBean w where w.jobId = :id"),
083
084 @NamedQuery(name = "GET_SLA_SUMMARY_EVENTPROCESSED_LAST_MODIFIED",
085         query = "select w.eventProcessed, w.lastModifiedTS from SLASummaryBean w where w.jobId = :id"),
086
087 @NamedQuery(name = "GET_SLA_SUMMARY_ALL", query = "select OBJECT(w) from SLASummaryBean w")
088
089})
090
091/**
092 * Class to store all the SLA related details (summary) per job
093 */
094public class SLASummaryBean implements JsonBean {
095
096    @Id
097    @Basic
098    @Column(name = "job_id")
099    private String jobId;
100
101    @Basic
102    @Index
103    @Column(name = "parent_id")
104    private String parentId;
105
106    @Basic
107    @Index
108    @Column(name = "app_name")
109    private String appName;
110
111    @Basic
112    @Column(name = "app_type")
113    private String appType;
114
115    @Basic
116    @Column(name = "user_name")
117    private String user;
118
119    @Basic
120    @Column(name = "created_time")
121    private Timestamp createdTimeTS = null;
122
123    @Basic
124    @Index
125    @Column(name = "nominal_time")
126    private Timestamp nominalTimeTS = null;
127
128    @Basic
129    @Column(name = "expected_start")
130    private Timestamp expectedStartTS = null;
131
132    @Basic
133    @Column(name = "expected_end")
134    private Timestamp expectedEndTS = null;
135
136    @Basic
137    @Column(name = "expected_duration")
138    private long expectedDuration = -1;
139
140    @Basic
141    @Column(name = "actual_start")
142    private Timestamp actualStartTS = null;
143
144    @Basic
145    @Column(name = "actual_end")
146    private Timestamp actualEndTS = null;
147
148    @Basic
149    @Column(name = "actual_duration")
150    private long actualDuration = -1;
151
152    @Basic
153    @Column(name = "job_status")
154    private String jobStatus;
155
156    @Basic
157    @Column(name = "event_status")
158    private String eventStatus;
159
160    @Basic
161    @Column(name = "sla_status")
162    private String slaStatus;
163
164    @Basic
165    @Index
166    @Column(name = "event_processed")
167    private byte eventProcessed = 0;
168
169    @Basic
170    @Index
171    @Column(name = "last_modified")
172    private Timestamp lastModifiedTS = null;
173
174    public SLASummaryBean() {
175    }
176
177    public SLASummaryBean(SLACalcStatus slaCalc) {
178        SLARegistrationBean reg = slaCalc.getSLARegistrationBean();
179        setId(slaCalc.getId());
180        setAppName(reg.getAppName());
181        setAppType(reg.getAppType());
182        setNominalTime(reg.getNominalTime());
183        setExpectedStart(reg.getExpectedStart());
184        setExpectedEnd(reg.getExpectedEnd());
185        setExpectedDuration(reg.getExpectedDuration());
186        setJobStatus(slaCalc.getJobStatus());
187        setSLAStatus(slaCalc.getSLAStatus());
188        setEventStatus(slaCalc.getEventStatus());
189        setLastModifiedTime(slaCalc.getLastModifiedTime());
190        setUser(reg.getUser());
191        setParentId(reg.getParentId());
192        setEventProcessed(slaCalc.getEventProcessed());
193        setActualDuration(slaCalc.getActualDuration());
194        setActualEnd(slaCalc.getActualEnd());
195        setActualStart(slaCalc.getActualStart());
196    }
197
198    public String getId() {
199        return jobId;
200    }
201
202    public void setId(String jobId) {
203        this.jobId = jobId;
204    }
205
206    public String getParentId() {
207        return parentId;
208    }
209
210    public void setParentId(String parentId) {
211        this.parentId = parentId;
212    }
213
214    public Timestamp getCreatedTimestamp() {
215        return createdTimeTS;
216    }
217
218    public void setCreatedTimestamp(Timestamp createdTime) {
219        this.createdTimeTS = createdTime;
220    }
221
222    public Date getCreatedTime() {
223        return DateUtils.toDate(createdTimeTS);
224    }
225
226    public void setCreatedTime(Date createdTime) {
227        this.createdTimeTS = DateUtils.convertDateToTimestamp(createdTime);
228    }
229
230    public Date getNominalTime() {
231        return DateUtils.toDate(nominalTimeTS);
232    }
233
234    public Timestamp getNominalTimestamp() {
235        return this.nominalTimeTS;
236    }
237
238    public void setNominalTime(Date nominalTime) {
239        this.nominalTimeTS = DateUtils.convertDateToTimestamp(nominalTime);
240    }
241
242
243    public Date getExpectedStart() {
244        return DateUtils.toDate(expectedStartTS);
245    }
246
247    public Timestamp getExpectedStartTimestamp() {
248        return this.expectedStartTS;
249    }
250
251    public void setExpectedStart(Date expectedStart) {
252        this.expectedStartTS = DateUtils.convertDateToTimestamp(expectedStart);
253    }
254
255    public Date getExpectedEnd() {
256        return DateUtils.toDate(expectedEndTS);
257    }
258
259    public Timestamp getExpectedEndTimestamp() {
260        return this.expectedEndTS;
261    }
262    public void setExpectedEnd(Date expectedEnd) {
263        this.expectedEndTS = DateUtils.convertDateToTimestamp(expectedEnd);
264    }
265
266    public long getExpectedDuration() {
267        return expectedDuration;
268    }
269
270    public void setExpectedDuration(long expectedDuration) {
271        this.expectedDuration = expectedDuration;
272    }
273
274    public Date getActualStart() {
275        return DateUtils.toDate(actualStartTS);
276    }
277
278    public Timestamp getActualStartTimestamp() {
279        return this.actualStartTS;
280    }
281
282    public void setActualStart(Date actualStart) {
283        this.actualStartTS = DateUtils.convertDateToTimestamp(actualStart);
284    }
285
286    public Date getActualEnd() {
287        return DateUtils.toDate(actualEndTS);
288    }
289
290    public Timestamp getActualEndTimestamp() {
291        return this.actualEndTS;
292    }
293
294    public void setActualEnd(Date actualEnd) {
295        this.actualEndTS = DateUtils.convertDateToTimestamp(actualEnd);
296    }
297
298    public long getActualDuration() {
299        return actualDuration;
300    }
301
302    public void setActualDuration(long actualDuration) {
303        this.actualDuration = actualDuration;
304    }
305
306    public String getJobStatus() {
307        return jobStatus;
308    }
309
310    public void setJobStatus(String status) {
311        this.jobStatus = status;
312    }
313
314    public SLAEvent.EventStatus getEventStatus() {
315        return (eventStatus != null ? SLAEvent.EventStatus.valueOf(eventStatus) : null);
316    }
317
318    public void setEventStatus(SLAEvent.EventStatus eventStatus) {
319        this.eventStatus = (eventStatus != null ? eventStatus.name() : null);
320    }
321
322    public SLAEvent.SLAStatus getSLAStatus() {
323        return (slaStatus != null ? SLAEvent.SLAStatus.valueOf(slaStatus) : null);
324    }
325
326    public String getSLAStatusString() {
327        return slaStatus;
328    }
329
330    public String getEventStatusString() {
331        return eventStatus;
332    }
333
334    public void setSLAStatus(SLAEvent.SLAStatus stage) {
335        this.slaStatus = (stage != null ? stage.name() : null);
336    }
337
338    public String getUser() {
339        return user;
340    }
341
342    public void setUser(String user) {
343        this.user = user;
344    }
345
346    public String getAppName() {
347        return appName;
348    }
349
350    public void setAppName(String appName) {
351        this.appName = appName;
352    }
353
354    public AppType getAppType() {
355        return AppType.valueOf(appType);
356    }
357
358    public void setAppType(AppType appType) {
359        this.appType = appType.toString();
360    }
361
362    public byte getEventProcessed() {
363        return eventProcessed;
364    }
365
366    public void setEventProcessed(int eventProcessed) {
367        this.eventProcessed = (byte)eventProcessed;
368    }
369
370    public Date getLastModifiedTime() {
371        return DateUtils.toDate(lastModifiedTS);
372    }
373
374    public Timestamp getLastModifiedTimestamp() {
375        return this.lastModifiedTS;
376    }
377
378    public void setLastModifiedTime(Date lastModified) {
379        this.lastModifiedTS = DateUtils.convertDateToTimestamp(lastModified);
380    }
381
382    @Override
383    public JSONObject toJSONObject() {
384        return toJSONObject(null);
385    }
386
387    @Override
388    @SuppressWarnings("unchecked")
389    public JSONObject toJSONObject(String timeZoneId) {
390        JSONObject json = new JSONObject();
391        Map<EventStatus,Long> eventMap = calculateEventStatus();
392        StringBuilder eventStatusStr = new StringBuilder();
393        boolean first = true;
394        for(EventStatus e: eventMap.keySet()) {
395            if(!first) {
396                eventStatusStr.append(",");
397            }
398            eventStatusStr.append(e.toString());
399            first = false;
400        }
401        json.put(JsonTags.SLA_SUMMARY_ID, jobId);
402        if (parentId != null) {
403            json.put(JsonTags.SLA_SUMMARY_PARENT_ID, parentId);
404        }
405        json.put(JsonTags.SLA_SUMMARY_APP_NAME, appName);
406        json.put(JsonTags.SLA_SUMMARY_APP_TYPE, appType);
407        json.put(JsonTags.SLA_SUMMARY_USER, user);
408        json.put(JsonTags.SLA_SUMMARY_NOMINAL_TIME, getTimeOnTimeZone(nominalTimeTS, timeZoneId));
409        if (expectedStartTS != null) {
410            json.put(JsonTags.SLA_SUMMARY_EXPECTED_START, getTimeOnTimeZone(expectedStartTS, timeZoneId));
411        } else {
412            json.put(JsonTags.SLA_SUMMARY_EXPECTED_START, null);
413        }
414
415        if (actualStartTS != null) {
416            json.put(JsonTags.SLA_SUMMARY_ACTUAL_START, getTimeOnTimeZone(actualStartTS, timeZoneId));
417        }
418        else {
419            json.put(JsonTags.SLA_SUMMARY_ACTUAL_START, null);
420        }
421        Long startDelay = eventMap.get(EventStatus.START_MET) != null ? eventMap.get(EventStatus.START_MET) : eventMap
422                .get(EventStatus.START_MISS);
423        if (startDelay != null) {
424            json.put(JsonTags.SLA_SUMMARY_START_DELAY, startDelay);
425        }
426        if (expectedEndTS != null ) {
427            json.put(JsonTags.SLA_SUMMARY_EXPECTED_END, getTimeOnTimeZone(expectedEndTS,timeZoneId));
428        } else {
429            json.put(JsonTags.SLA_SUMMARY_ACTUAL_END, null);
430        }
431        if (actualEndTS != null) {
432            json.put(JsonTags.SLA_SUMMARY_ACTUAL_END, getTimeOnTimeZone(actualEndTS,timeZoneId));
433        }
434        else {
435            json.put(JsonTags.SLA_SUMMARY_ACTUAL_END, null);
436        }
437        Long endDelay = eventMap.get(EventStatus.END_MET) != null ? eventMap.get(EventStatus.END_MET) : eventMap
438                .get(EventStatus.END_MISS);
439        if (endDelay != null) {
440            json.put(JsonTags.SLA_SUMMARY_END_DELAY, endDelay);
441        }
442        json.put(JsonTags.SLA_SUMMARY_EXPECTED_DURATION, expectedDuration);
443        if (actualDuration == -1 && expectedDuration != -1 && actualStartTS != null) {
444            long currentDur = new Date().getTime() - actualStartTS.getTime();
445            json.put(JsonTags.SLA_SUMMARY_ACTUAL_DURATION, currentDur);
446        }
447        else {
448            json.put(JsonTags.SLA_SUMMARY_ACTUAL_DURATION, actualDuration);
449        }
450        Long durationDelay = eventMap.get(EventStatus.DURATION_MET) != null ? eventMap.get(EventStatus.DURATION_MET)
451                : eventMap.get(EventStatus.DURATION_MISS);
452        if (durationDelay != null) {
453            json.put(JsonTags.SLA_SUMMARY_DURATION_DELAY, durationDelay / (1000 * 60));
454        }
455        json.put(JsonTags.SLA_SUMMARY_JOB_STATUS, jobStatus);
456        json.put(JsonTags.SLA_SUMMARY_SLA_STATUS, slaStatus);
457        json.put(JsonTags.SLA_SUMMARY_EVENT_STATUS, eventStatusStr.toString());
458        json.put(JsonTags.SLA_SUMMARY_LAST_MODIFIED, getTimeOnTimeZone(lastModifiedTS, timeZoneId));
459        return json;
460    }
461
462    private Object getTimeOnTimeZone(Timestamp ts, String timeZoneId) {
463        Object ret = null;
464        if(timeZoneId == null) {
465            ret = new Long(String.valueOf(ts.getTime()));
466        } else {
467            ret = JsonUtils.formatDateRfc822(ts, timeZoneId);
468        }
469        return ret;
470    }
471
472    private Map<EventStatus, Long> calculateEventStatus() {
473        Map<EventStatus, Long> events = new HashMap<EventStatus, Long>();
474        if (expectedStartTS != null) {
475            if (actualStartTS != null) {
476                long diff = (actualStartTS.getTime() - expectedStartTS.getTime()) / (1000 * 60);
477                if (diff > 0) {
478                    events.put(EventStatus.START_MISS, diff);
479                }
480                else {
481                    events.put(EventStatus.START_MET, diff);
482                }
483            }
484            else {
485                long diff = (new Date().getTime() - expectedStartTS.getTime()) / (1000 * 60);
486                if (diff > 0) {
487                    events.put(EventStatus.START_MISS, diff);
488                }
489            }
490        }
491        if (expectedDuration != -1) {
492            if (actualDuration != -1) {
493                long diff = actualDuration - expectedDuration;
494                if (diff > 0) {
495                    events.put(EventStatus.DURATION_MISS, diff);
496                }
497                else {
498                    events.put(EventStatus.DURATION_MET, diff);
499                }
500            }
501            else {
502                if (actualStartTS != null) {
503                    long currentDur = new Date().getTime() - actualStartTS.getTime();
504                    if (expectedDuration < currentDur) {
505                        events.put(EventStatus.DURATION_MISS, currentDur - expectedDuration);
506                    }
507                }
508            }
509        }
510        if (expectedEndTS != null) {
511            if (actualEndTS != null) {
512                long diff = (actualEndTS.getTime() - expectedEndTS.getTime()) / (1000 * 60);
513                if (diff > 0) {
514                    events.put(EventStatus.END_MISS, diff);
515                }
516                else {
517                    events.put(EventStatus.END_MET, diff);
518                }
519            }
520            else {
521                long diff = (new Date().getTime() - expectedEndTS.getTime()) / (1000 * 60);
522                if (diff > 0) {
523                    events.put(EventStatus.END_MISS, diff);
524                }
525            }
526        }
527        return events;
528    }
529    /**
530     * Convert a sla summary list into a json object.
531     *
532     * @param slaSummaryList sla summary list.
533     * @param timeZoneId time zone to use for dates in the JSON array.
534     * @return the corresponding JSON object.
535     */
536    @SuppressWarnings("unchecked")
537    public static JSONObject toJSONObject(List<? extends SLASummaryBean> slaSummaryList, String timeZoneId) {
538        JSONObject json = new JSONObject();
539        JSONArray array = new JSONArray();
540        if (slaSummaryList != null) {
541            for (SLASummaryBean summary : slaSummaryList) {
542                array.add(summary.toJSONObject(timeZoneId));
543            }
544        }
545        json.put(JsonTags.SLA_SUMMARY_LIST, array);
546        return json;
547    }
548
549    @SuppressWarnings("unchecked")
550    public static JSONObject toJSONObject(List<? extends SLASummaryBean> slaSummaryList,
551            Map<String, Map<String, String>> slaConfigMap, String timeZoneId) {
552        JSONObject json = new JSONObject();
553        JSONArray array = new JSONArray();
554        if (slaSummaryList != null) {
555            for (SLASummaryBean summary : slaSummaryList) {
556                JSONObject slaJson = summary.toJSONObject(timeZoneId);
557                String slaAlertStatus = "";
558                if (slaConfigMap.containsKey(summary.getId())) {
559                    slaAlertStatus = slaConfigMap.get(summary.getId()).containsKey(OozieClient.SLA_DISABLE_ALERT) ? "Disabled"
560                            : "Enabled";
561                }
562                slaJson.put(JsonTags.SLA_ALERT_STATUS, slaAlertStatus);
563                array.add(slaJson);
564            }
565        }
566        json.put(JsonTags.SLA_SUMMARY_LIST, array);
567        return json;
568    }
569}