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}