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.text.ParseException;
022import java.util.Date;
023
024import org.apache.oozie.AppType;
025import org.apache.oozie.ErrorCode;
026import org.apache.oozie.client.event.SLAEvent.EventStatus;
027import org.apache.oozie.command.CommandException;
028import org.apache.oozie.executor.jpa.JPAExecutorException;
029import org.apache.oozie.executor.jpa.SLARegistrationQueryExecutor;
030import org.apache.oozie.executor.jpa.SLARegistrationQueryExecutor.SLARegQuery;
031import org.apache.oozie.service.JPAService;
032import org.apache.oozie.service.ServiceException;
033import org.apache.oozie.service.Services;
034import org.apache.oozie.sla.SLARegistrationBean;
035import org.apache.oozie.sla.service.SLAService;
036import org.apache.oozie.util.DateUtils;
037import org.apache.oozie.util.XLog;
038import org.apache.oozie.util.XmlUtils;
039import org.jdom.Element;
040
041public class SLAOperations {
042
043    private static final String NOMINAL_TIME = "nominal-time";
044    private static final String SHOULD_START = "should-start";
045    private static final String SHOULD_END = "should-end";
046    private static final String MAX_DURATION = "max-duration";
047    private static final String ALERT_EVENTS = "alert-events";
048
049    public static SLARegistrationBean createSlaRegistrationEvent(Element eSla, String jobId, String parentId,
050            AppType appType, String user, String appName, XLog log, boolean rerun) throws CommandException {
051        if (eSla == null || !SLAService.isEnabled()) {
052            log.debug("Not registering SLA for job [{0}]. Sla-Xml null OR SLAService not enabled", jobId);
053            return null;
054        }
055        SLARegistrationBean sla = new SLARegistrationBean();
056
057        // Setting nominal time
058        String strNominalTime = getTagElement(eSla, NOMINAL_TIME);
059        if (strNominalTime == null || strNominalTime.length() == 0) {
060            throw new CommandException(ErrorCode.E1101, NOMINAL_TIME);
061        }
062        Date nominalTime;
063        try {
064            nominalTime = DateUtils.parseDateOozieTZ(strNominalTime);
065            sla.setNominalTime(nominalTime);
066        }
067        catch (ParseException pex) {
068            throw new CommandException(ErrorCode.E0302, strNominalTime, pex);
069        }
070
071        // Setting expected start time
072        String strExpectedStart = getTagElement(eSla, SHOULD_START);
073        if (strExpectedStart != null) {
074            float expectedStart = Float.parseFloat(strExpectedStart);
075            if (expectedStart < 0) {
076                throw new CommandException(ErrorCode.E0302, strExpectedStart, "for SLA Expected start time");
077            }
078            else {
079                Date expectedStartTime = new Date(nominalTime.getTime() + (long) (expectedStart * 60 * 1000));
080                sla.setExpectedStart(expectedStartTime);
081            }
082        }
083
084        // Setting expected end time
085        String strExpectedEnd = getTagElement(eSla, SHOULD_END);
086        if (strExpectedEnd == null || strExpectedEnd.length() == 0) {
087            throw new CommandException(ErrorCode.E1101, SHOULD_END);
088        }
089        float expectedEnd = Float.parseFloat(strExpectedEnd);
090        if (expectedEnd < 0) {
091            throw new CommandException(ErrorCode.E0302, strExpectedEnd, "for SLA Expected end time");
092        }
093        else {
094            Date expectedEndTime = new Date(nominalTime.getTime() + (long) (expectedEnd * 60 * 1000));
095            sla.setExpectedEnd(expectedEndTime);
096        }
097
098        // Setting expected duration in milliseconds
099        String expectedDurationStr = getTagElement(eSla, MAX_DURATION);
100        if (expectedDurationStr != null && expectedDurationStr.length() > 0) {
101            float expectedDuration = Float.parseFloat(expectedDurationStr);
102            if (expectedDuration > 0) {
103                sla.setExpectedDuration((long) (expectedDuration * 60 * 1000));
104            }
105        }
106        else if (sla.getExpectedStart() != null) {
107            sla.setExpectedDuration(sla.getExpectedEnd().getTime() - sla.getExpectedStart().getTime());
108        }
109
110        // Parse desired alert-types i.e. start-miss, end-miss, start-met etc..
111        String alertEvents = getTagElement(eSla, ALERT_EVENTS);
112        if (alertEvents != null) {
113            String events[] = alertEvents.split(",");
114            StringBuilder alertsStr = new StringBuilder();
115            for (int i = 0; i < events.length; i++) {
116                String event = events[i].trim().toUpperCase();
117                try {
118                    EventStatus.valueOf(event);
119                }
120                catch (IllegalArgumentException iae) {
121                    XLog.getLog(SLAService.class).warn(
122                            "Invalid value: [" + event + "]" + " for SLA Alert-event. Should be one of "
123                                    + EventStatus.values() + ". Setting it to default [" + EventStatus.END_MISS.name()
124                                    + "]");
125                    event = EventStatus.END_MISS.name();
126                }
127                alertsStr.append(event).append(",");
128            }
129            sla.setAlertEvents(alertsStr.toString().substring(0, alertsStr.lastIndexOf(",")));
130        }
131
132        // Other sla config
133        sla.setNotificationMsg(getTagElement(eSla, "notification-msg"));
134        sla.setAlertContact(getTagElement(eSla, "alert-contact"));
135        sla.setUpstreamApps(getTagElement(eSla, "upstream-apps"));
136
137        // Oozie defined
138        sla.setId(jobId);
139        sla.setAppType(appType);
140        sla.setAppName(appName);
141        sla.setUser(user);
142        sla.setParentId(parentId);
143
144        SLAService slaService = Services.get().get(SLAService.class);
145        try {
146            if (!rerun) {
147                slaService.addRegistrationEvent(sla);
148            }
149            else {
150                slaService.updateRegistrationEvent(sla);
151            }
152        }
153        catch (ServiceException e) {
154            throw new CommandException(ErrorCode.E1007, " id " + jobId, e.getMessage(), e);
155        }
156
157        log.debug("Job [{0}] reg for SLA. Size of Sla Xml = [{1}]", jobId, XmlUtils.prettyPrint(eSla).toString().length());
158        return sla;
159    }
160
161    /**
162     * Retrieve registration event
163     * @param jobId the jobId
164     * @throws CommandException
165     * @throws JPAExecutorException
166     */
167    public static void updateRegistrationEvent(String jobId) throws CommandException, JPAExecutorException {
168        JPAService jpaService = Services.get().get(JPAService.class);
169        SLAService slaService = Services.get().get(SLAService.class);
170        try {
171            SLARegistrationBean reg = SLARegistrationQueryExecutor.getInstance().get(SLARegQuery.GET_SLA_REG_ALL, jobId);
172            if (reg != null) { //handle coord rerun with different config without sla
173                slaService.updateRegistrationEvent(reg);
174            }
175        }
176        catch (ServiceException e) {
177            throw new CommandException(ErrorCode.E1007, " id " + jobId, e.getMessage(), e);
178        }
179
180    }
181
182    /*
183     * parentId null
184     */
185    public static SLARegistrationBean createSlaRegistrationEvent(Element eSla, String jobId, AppType appType,
186            String user, String appName, XLog log) throws CommandException {
187        return createSlaRegistrationEvent(eSla, jobId, null, appType, user, appName, log, false);
188    }
189
190    /*
191     * appName null
192     */
193    public static SLARegistrationBean createSlaRegistrationEvent(Element eSla, String jobId, String parentId,
194            AppType appType, String user, XLog log) throws CommandException {
195        return createSlaRegistrationEvent(eSla, jobId, parentId, appType, user, null, log, false);
196    }
197
198    /*
199     * parentId + appName null
200     */
201    public static SLARegistrationBean createSlaRegistrationEvent(Element eSla, String jobId, AppType appType,
202            String user, XLog log) throws CommandException {
203        return createSlaRegistrationEvent(eSla, jobId, null, appType, user, null, log, false);
204    }
205
206    private static String getTagElement(Element elem, String tagName) {
207        if (elem != null && elem.getChild(tagName, elem.getNamespace("sla")) != null) {
208            return elem.getChild(tagName, elem.getNamespace("sla")).getText().trim();
209        }
210        else {
211            return null;
212        }
213    }
214
215}