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