/** * */ package com.thinkbiganalytics.metadata.sla.api.core; /*- * #%L * thinkbig-sla-metrics-default * %% * Copyright (C) 2017 ThinkBig Analytics * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import com.cronutils.descriptor.CronDescriptor; import com.cronutils.model.Cron; import com.cronutils.model.CronType; import com.cronutils.model.definition.CronDefinition; import com.cronutils.model.definition.CronDefinitionBuilder; import com.cronutils.parser.CronParser; import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.common.base.MoreObjects; import com.thinkbiganalytics.metadata.sla.api.Metric; import com.thinkbiganalytics.metadata.sla.api.ServiceLevelAgreementMetric; import com.thinkbiganalytics.policy.PolicyProperty; import com.thinkbiganalytics.policy.PolicyPropertyRef; import com.thinkbiganalytics.policy.PolicyPropertyTypes; import com.thinkbiganalytics.policy.PropertyLabelValue; import com.thinkbiganalytics.scheduler.util.TimerToCronExpression; import org.joda.time.Period; import org.quartz.CronExpression; import java.text.ParseException; import java.util.Locale; /** * SLA metric to ensure a feed gets executed by a specified time * This will be exposed to the User Interface since it is annotated with {@link ServiceLevelAgreementMetric} */ @ServiceLevelAgreementMetric(name = "Feed Processing deadline", description = "Ensure a Feed processes data by a specified time") public class FeedOnTimeArrivalMetric implements Metric { @PolicyProperty(name = "FeedName", type = PolicyPropertyTypes.PROPERTY_TYPE.feedSelect, required = true, value = PolicyPropertyTypes.CURRENT_FEED_VALUE) private String feedName; @PolicyProperty(name = "ExpectedDeliveryTime", displayName = "Expected Delivery Time", type = PolicyPropertyTypes.PROPERTY_TYPE.cron, hint = "Cron Expression for when you expect to receive this data", value = "0 0 12 1/1 * ? *", required = true) private String cronString; @PolicyProperty(name = "NoLaterThanTime", displayName = "No later than time", type = PolicyPropertyTypes.PROPERTY_TYPE.string, pattern = "^\\d+$", patternInvalidMessage = "The value must be numeric digits", hint = "Number specifying the amount of time allowed after the Expected Delivery Time", group = "lateTime", required = true) private Integer lateTime; @PolicyProperty(name = "NoLaterThanUnits", displayName = "Units", type = PolicyPropertyTypes.PROPERTY_TYPE.select, group = "lateTime", labelValues = {@PropertyLabelValue(label = "Days", value = "days"), @PropertyLabelValue(label = "Hours", value = "hrs"), @PropertyLabelValue(label = "Minutes", value = "min"), @PropertyLabelValue(label = "Seconds", value = "sec")}, required = true) private String lateUnits; @JsonIgnore private CronExpression expectedExpression; /** * lateTime + lateUnits as a Jodatime period object **/ private Period latePeriod; public FeedOnTimeArrivalMetric() { } public FeedOnTimeArrivalMetric(@PolicyPropertyRef(name = "FeedName") String feedName, @PolicyPropertyRef(name = "ExpectedDeliveryTime") String cronString, @PolicyPropertyRef(name = "NoLaterThanTime") Integer lateTime, @PolicyPropertyRef(name = "NoLaterThanUnits") String lateUnits) throws ParseException { this.feedName = feedName; this.cronString = cronString; this.lateTime = lateTime; this.lateUnits = lateUnits; this.expectedExpression = new CronExpression(this.cronString); this.latePeriod = TimerToCronExpression.timerStringToPeriod(this.lateTime + " " + this.lateUnits); } public FeedOnTimeArrivalMetric(String feedName, CronExpression expectedExpression, Period latePeriod ) { super(); this.feedName = feedName; this.expectedExpression = expectedExpression; this.latePeriod = latePeriod; } /* (non-Javadoc) * @see com.thinkbiganalytics.metadata.sla.api.Metric#getDescription() */ @Override public String getDescription() { StringBuilder bldr = new StringBuilder("Data expected from feed "); bldr.append("\"").append(this.feedName).append("\" ") .append(generateCronDescription(this.getExpectedExpression().toString())) .append(", and no more than ").append(this.latePeriod.getHours()).append(" hours late"); return bldr.toString(); } @Override public String toString() { return MoreObjects.toStringHelper(this) .omitNullValues() .add("feedName", this.feedName) .add("expectedExpression", this.expectedExpression) .add("latePeriod", this.latePeriod) .toString(); } public String getFeedName() { return feedName; } public void setFeedName(String feedName) { this.feedName = feedName; } public CronExpression getExpectedExpression() { if (this.expectedExpression == null && this.cronString != null) { try { this.expectedExpression = new CronExpression(this.cronString); } catch (ParseException e) { throw new RuntimeException(e); } } return expectedExpression; } public void setExpectedExpression(CronExpression expectedExpression) { this.expectedExpression = expectedExpression; } public Period getLatePeriod() { return latePeriod; } public void setLatePeriod(Period latePeriod) { this.latePeriod = latePeriod; } private String generateCronDescription(String cronExp) { CronDefinition quartzDef = CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ); CronParser parser = new CronParser(quartzDef); Cron c = parser.parse(cronExp); return CronDescriptor.instance(Locale.US).describe(c); } }