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