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}