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