/**
*
*/
package com.thinkbiganalytics.metadata.sla.spi.core;
/*-
* #%L
* thinkbig-sla-core
* %%
* 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.thinkbiganalytics.metadata.sla.api.AssessmentResult;
import com.thinkbiganalytics.metadata.sla.api.Metric;
import com.thinkbiganalytics.metadata.sla.api.MetricAssessment;
import com.thinkbiganalytics.metadata.sla.api.Obligation;
import com.thinkbiganalytics.metadata.sla.api.ObligationAssessment;
import com.thinkbiganalytics.metadata.sla.api.ObligationGroup;
import com.thinkbiganalytics.metadata.sla.api.ServiceLevelAgreement;
import com.thinkbiganalytics.metadata.sla.api.ServiceLevelAssessment;
import com.thinkbiganalytics.metadata.sla.spi.AssessorNotFoundException;
import com.thinkbiganalytics.metadata.sla.spi.MetricAssessmentBuilder;
import com.thinkbiganalytics.metadata.sla.spi.MetricAssessor;
import com.thinkbiganalytics.metadata.sla.spi.ObligationAssessmentBuilder;
import com.thinkbiganalytics.metadata.sla.spi.ObligationAssessor;
import com.thinkbiganalytics.metadata.sla.spi.ServiceLevelAssessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
*/
public class SimpleServiceLevelAssessor implements ServiceLevelAssessor {
private static final Logger Log = LoggerFactory.getLogger(SimpleServiceLevelAssessor.class);
private Set<ObligationAssessor<? extends Obligation>> obligationAssessors;
private Set<MetricAssessor<? extends Metric, ? extends Serializable>> metricAssessors;
private ObligationAssessor<? extends Obligation> defaultObligationAssessor;
private Map<ServiceLevelAgreement.ID, ServiceLevelAssessment> lastAssessments = new HashMap<>();
/**
*
*/
public SimpleServiceLevelAssessor() {
this.obligationAssessors = Collections.synchronizedSet(new HashSet<ObligationAssessor<? extends Obligation>>());
this.metricAssessors = Collections.synchronizedSet(new HashSet<MetricAssessor<? extends Metric, ? extends Serializable>>());
this.defaultObligationAssessor = createDefaultObligationAssessor();
}
/*
* (non-Javadoc)
*
* @see
* com.thinkbiganalytics.metadata.sla.spi.ServiceLevelAssessor#assess(com.
* thinkbiganalytics.metadata.sla.api.ServiceLevelAgreement)
*/
@Override
public ServiceLevelAssessment assess(ServiceLevelAgreement sla) {
Log.info("Assessing SLA: {}", sla.getName());
AssessmentResult combinedResult = AssessmentResult.FAILURE;
try {
SimpleServiceLevelAssessment slaAssessment = new SimpleServiceLevelAssessment(sla);
List<ObligationGroup> groups = sla.getObligationGroups();
for (ObligationGroup group : groups) {
ObligationGroup.Condition condition = group.getCondition();
AssessmentResult groupResult = AssessmentResult.SUCCESS;
for (Obligation ob : group.getObligations()) {
ObligationAssessment obAssessment = assess(ob);
slaAssessment.add(obAssessment);
groupResult = groupResult.max(obAssessment.getResult());
}
// Short-circuit required or sufficient if necessary.
switch (condition) {
case REQUIRED:
if (groupResult == AssessmentResult.FAILURE) {
return completeAssessment(slaAssessment, groupResult);
}
break;
case SUFFICIENT:
if (groupResult != AssessmentResult.FAILURE) {
return completeAssessment(slaAssessment, groupResult);
}
break;
default:
}
// Required condition but non-failure, sufficient condition but non-success, or optional condition:
// continue assessing groups and retain the best of the group results.
combinedResult = combinedResult.min(groupResult);
}
return completeAssessment(slaAssessment, combinedResult);
} finally {
Log.debug("Completed assessment of SLA {}: {}", sla.getName(), combinedResult);
}
}
/*
* (non-Javadoc)
*
* @see com.thinkbiganalytics.metadata.sla.spi.ServiceLevelAssessor#
* registerObligationAssessor(com.thinkbiganalytics.metadata.sla.spi.
* ObligationAssessor)
*/
@Override
public ObligationAssessor<? extends Obligation> registerObligationAssessor(ObligationAssessor<? extends Obligation> assessor) {
this.obligationAssessors.add(assessor);
return assessor;
}
/*
* (non-Javadoc)
*
* @see com.thinkbiganalytics.metadata.sla.spi.ServiceLevelAssessor#
* registerMetricAssessor(com.thinkbiganalytics.metadata.sla.spi.
* MetricAssessor)
*/
@Override
public MetricAssessor<? extends Metric, ? extends Serializable> registerMetricAssessor(MetricAssessor<? extends Metric, ? extends Serializable> assessor) {
this.metricAssessors.add(assessor);
return assessor;
}
protected ObligationAssessor<? extends Obligation> createDefaultObligationAssessor() {
return new DefaultObligationAssessor();
}
protected ObligationAssessor<? extends Obligation> findAssessor(Obligation obligation) {
synchronized (this.obligationAssessors) {
for (ObligationAssessor<? extends Obligation> assessor : this.obligationAssessors) {
if (assessor.accepts(obligation)) {
return assessor;
}
}
}
return this.defaultObligationAssessor;
}
@SuppressWarnings("unchecked")
protected <M extends Metric> MetricAssessor<M, ?> findAssessor(M metric) {
synchronized (this.metricAssessors) {
for (MetricAssessor<? extends Metric, ? extends Serializable> accessor : this.metricAssessors) {
if (accessor.accepts(metric)) {
return (MetricAssessor<M, ?>) accessor;
}
}
}
throw new AssessorNotFoundException(metric);
}
@Override
public ServiceLevelAssessment findLatestAssessment(ServiceLevelAgreement sla) {
return lastAssessments.get(sla.getId());
}
private ServiceLevelAssessment completeAssessment(SimpleServiceLevelAssessment slaAssessment, AssessmentResult result) {
slaAssessment.setResult(result);
if (result == AssessmentResult.SUCCESS) {
slaAssessment.setMessage("SLA assessment requirements were met");
} else {
slaAssessment.setMessage("At least one of the SLA obligations resulted in the status: " + result);
}
lastAssessments.put(slaAssessment.getAgreement().getId(), slaAssessment);
return slaAssessment;
}
private ObligationAssessment assess(Obligation ob) {
ObligationAssessmentBuilderImpl builder = new ObligationAssessmentBuilderImpl(ob);
@SuppressWarnings("unchecked")
ObligationAssessor<Obligation> assessor = (ObligationAssessor<Obligation>) findAssessor(ob);
Log.debug("Assessing obligation \"{}\" with assessor: {}", assessor);
assessor.assess(ob, builder);
return builder.build();
}
private class ObligationAssessmentBuilderImpl implements ObligationAssessmentBuilder {
private Obligation obligation;
private String message = "";
private AssessmentResult result = AssessmentResult.SUCCESS;
private Comparator<ObligationAssessment> comparator;
private List<Comparable<? extends Serializable>> comparables;
private SimpleObligationAssessment assessment;
public ObligationAssessmentBuilderImpl(Obligation obligation) {
this.obligation = obligation;
this.assessment = new SimpleObligationAssessment(obligation);
}
@Override
public ObligationAssessmentBuilder obligation(Obligation ob) {
this.obligation = ob;
return this;
}
@Override
public ObligationAssessmentBuilder result(AssessmentResult result) {
this.result = result;
return this;
}
@Override
public ObligationAssessmentBuilder message(String descr) {
this.message = descr;
return this;
}
@Override
public ObligationAssessmentBuilder comparator(Comparator<ObligationAssessment> comp) {
this.comparator = comp;
return this;
}
@Override
@SuppressWarnings("unchecked")
public ObligationAssessmentBuilder compareWith(final Comparable<? extends Serializable> value,
@SuppressWarnings("unchecked")
final Comparable<? extends Serializable>... otherValeus) {
;
this.comparables = new ArrayList<Comparable<? extends Serializable>>(Arrays.asList(value));
this.comparables.addAll(Arrays.asList(value));
return this;
}
@Override
public <M extends Metric> MetricAssessment<?> assess(M metric) {
MetricAssessor<M, ?> assessor = findAssessor(metric);
MetricAssessmentBuilderImpl builder = new MetricAssessmentBuilderImpl(metric);
assessor.assess(metric, builder);
MetricAssessment<?> metricAssmt = builder.build();
this.assessment.add(metricAssmt);
return metricAssmt;
}
protected ObligationAssessment build() {
this.assessment.setObligation(this.obligation);
this.assessment.setMessage(this.message);
this.assessment.setResult(this.result);
if (this.comparator != null) {
this.assessment.setComparator(this.comparator);
}
if (this.comparables != null) {
this.assessment.setComparables(this.comparables);
}
return this.assessment;
}
}
private class MetricAssessmentBuilderImpl<D extends Serializable> implements MetricAssessmentBuilder<D> {
private Metric metric;
private String message = "";
private AssessmentResult result = AssessmentResult.SUCCESS;
private D data;
private Comparator<MetricAssessment<D>> comparator;
private List<Comparable<? extends Serializable>> comparables;
public MetricAssessmentBuilderImpl(Metric metric) {
this.metric = metric;
}
@Override
public MetricAssessmentBuilder<D> metric(Metric metric) {
this.metric = metric;
return this;
}
@Override
public MetricAssessmentBuilder<D> message(String descr) {
this.message = descr;
return this;
}
@Override
public MetricAssessmentBuilder<D> result(AssessmentResult result) {
this.result = result;
return this;
}
@Override
public MetricAssessmentBuilder<D> data(D data) {
this.data = data;
return this;
}
@Override
public MetricAssessmentBuilder<D> comparitor(Comparator<MetricAssessment<D>> comp) {
this.comparator = comp;
return this;
}
@Override
@SuppressWarnings("unchecked")
public MetricAssessmentBuilder<D> compareWith(Comparable<? extends Serializable> value,
Comparable<? extends Serializable>... otherValues) {
this.comparables = new ArrayList<Comparable<? extends Serializable>>(Arrays.asList(value));
this.comparables.addAll(Arrays.asList(value));
return this;
}
protected MetricAssessment build() {
SimpleMetricAssessment<D> assessment = new SimpleMetricAssessment<D>(this.metric);
assessment.setMessage(this.message);
assessment.setResult(this.result);
assessment.setData(this.data);
if (this.comparator != null) {
assessment.setComparator(this.comparator);
}
if (this.comparables != null) {
assessment.setComparables(this.comparables);
}
return assessment;
}
}
protected class DefaultObligationAssessor implements ObligationAssessor<Obligation> {
@Override
public boolean accepts(Obligation obligation) {
// Accepts any obligations
return true;
}
@Override
public void assess(Obligation obligation, ObligationAssessmentBuilder builder) {
Set<MetricAssessment> metricAssessments = new HashSet<MetricAssessment>();
AssessmentResult result = AssessmentResult.SUCCESS;
// Iterate through and assess each metric.
// Obligation is considered successful if all metrics are successful
for (Metric metric : obligation.getMetrics()) {
Log.debug("Assessing metric: {}", metric);
MetricAssessment assessment = builder.assess(metric);
metricAssessments.add(assessment);
}
for (MetricAssessment ma : metricAssessments) {
result = result.max(ma.getResult());
}
String message = "The obligation requirements were met";
if (result != AssessmentResult.SUCCESS) {
message = "At least one metric assessment resulted in the status: " + result;
}
builder
.result(result)
.message(message)
.obligation(obligation);
}
}
}