/**
*
*/
package com.thinkbiganalytics.metadata.modeshape.sla;
/*-
* #%L
* thinkbig-metadata-modeshape
* %%
* 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.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.thinkbiganalytics.metadata.api.sla.FeedServiceLevelAgreementProvider;
import com.thinkbiganalytics.metadata.modeshape.BaseJcrProvider;
import com.thinkbiganalytics.metadata.modeshape.MetadataRepositoryException;
import com.thinkbiganalytics.metadata.modeshape.common.JcrEntity;
import com.thinkbiganalytics.metadata.modeshape.common.JcrPropertyConstants;
import com.thinkbiganalytics.metadata.modeshape.feed.JcrFeedPrecondition;
import com.thinkbiganalytics.metadata.modeshape.sla.JcrServiceLevelAgreement.SlaId;
import com.thinkbiganalytics.metadata.modeshape.support.JcrPropertyUtil;
import com.thinkbiganalytics.metadata.modeshape.support.JcrQueryUtil;
import com.thinkbiganalytics.metadata.modeshape.support.JcrUtil;
import com.thinkbiganalytics.metadata.sla.api.Metric;
import com.thinkbiganalytics.metadata.sla.api.Obligation;
import com.thinkbiganalytics.metadata.sla.api.ObligationGroup.Condition;
import com.thinkbiganalytics.metadata.sla.api.ServiceLevelAgreement;
import com.thinkbiganalytics.metadata.sla.api.ServiceLevelAgreement.ID;
import com.thinkbiganalytics.metadata.sla.api.ServiceLevelAgreementActionConfig;
import com.thinkbiganalytics.metadata.sla.api.ServiceLevelAgreementActionConfiguration;
import com.thinkbiganalytics.metadata.sla.spi.ObligationBuilder;
import com.thinkbiganalytics.metadata.sla.spi.ObligationGroupBuilder;
import com.thinkbiganalytics.metadata.sla.spi.ServiceLevelAgreementBuilder;
import com.thinkbiganalytics.metadata.sla.spi.ServiceLevelAgreementCheck;
import com.thinkbiganalytics.metadata.sla.spi.ServiceLevelAgreementCheckBuilder;
import com.thinkbiganalytics.metadata.sla.spi.ServiceLevelAgreementProvider;
import org.modeshape.jcr.api.JcrTools;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.inject.Inject;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.query.QueryResult;
/**
*
*/
public class JcrServiceLevelAgreementProvider extends BaseJcrProvider<ServiceLevelAgreement, ServiceLevelAgreement.ID> implements ServiceLevelAgreementProvider {
public static final String SLA_PATH = "/metadata/sla";
private final JcrTools jcrTools = new JcrTools();
@Inject
private FeedServiceLevelAgreementProvider feedServiceLevelAgreementProvider;
@Override
public Class<? extends ServiceLevelAgreement> getEntityClass() {
return JcrServiceLevelAgreement.class;
}
@Override
public Class<? extends JcrEntity> getJcrEntityClass() {
return JcrServiceLevelAgreement.class;
}
@Override
public String getNodeType(Class<? extends JcrEntity> jcrEntityType) {
return "tba:sla";
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.sla.spi.ServiceLevelAgreementProvider#resolve(java.io.Serializable)
*/
@Override
public ID resolve(Serializable ser) {
return resolveId(ser);
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.sla.spi.ServiceLevelAgreementProvider#getAgreements()
*/
@Override
/**
* Find all Agreements that are not Preconditions
*/
public List<ServiceLevelAgreement> getAgreements() {
try {
Session session = getSession();
Node slasNode = session.getNode(SLA_PATH);
@SuppressWarnings("unchecked")
Iterator<Node> itr = (Iterator<Node>) slasNode.getNodes("sla-*");
return Lists.newArrayList(Iterators.transform(itr, (slaNode) -> {
return JcrUtil.createJcrObject(slaNode, JcrServiceLevelAgreement.class);
}));
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to retrieve the ServiceLevelAgreements", e);
}
}
/**
* Return All SLAs that are not Precondition SLAs
*/
public List<ServiceLevelAgreement> getNonPreconditionAgreements() {
try {
//query for the SLAs
String query = "SELECT * FROM [" + getNodeType(getJcrEntityClass()) + "] as sla "
+ "LEFT JOIN [" + JcrFeedPrecondition.NODE_TYPE + "] as precondition on precondition.[" + JcrFeedPrecondition.SLA + "] = sla.[jcr:uuid] "
+ " WHERE precondition.[jcr:uuid] is NULL ";
QueryResult result = JcrQueryUtil.query(getSession(), query, null);
return JcrQueryUtil.queryRowItrNodeResultToList(result, ServiceLevelAgreement.class, "sla");
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to retrieve the obligation nodes", e);
}
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.sla.spi.ServiceLevelAgreementProvider#getAgreement(com.thinkbiganalytics.metadata.sla.api.ServiceLevelAgreement.ID)
*/
@Override
public ServiceLevelAgreement getAgreement(ID id) {
try {
Session session = getSession();
SlaId slaId = (SlaId) id;
return new JcrServiceLevelAgreement(session.getNodeByIdentifier(slaId.getIdValue()));
} catch (ItemNotFoundException e) {
return null;
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to retrieve the SLA node", e);
}
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.sla.spi.ServiceLevelAgreementProvider#findAgreementByName(java.lang.String)
*/
@Override
public ServiceLevelAgreement findAgreementByName(String slaName) {
String query = "SELECT * FROM [" + getNodeType(getJcrEntityClass()) + "] as sla WHERE sla.[" + JcrPropertyConstants.TITLE + "] = $slaName";
return JcrQueryUtil.findFirst(getSession(), query, ImmutableMap.of("slaName", slaName), getEntityClass());
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.sla.spi.ServiceLevelAgreementProvider#removeAgreement(com.thinkbiganalytics.metadata.sla.api.ServiceLevelAgreement.ID)
*/
@Override
public boolean removeAgreement(ID id) {
try {
Session session = getSession();
SlaId slaId = (SlaId) id;
Node slaNode = session.getNodeByIdentifier(slaId.getIdValue());
if (slaNode != null) {
JcrServiceLevelAgreement sla = new JcrServiceLevelAgreement(slaNode);
//remove any other relationships
feedServiceLevelAgreementProvider.removeAllRelationships(id);
slaNode.remove();
}
return true;
} catch (ItemNotFoundException e) {
return false;
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to retrieve the SLA node", e);
}
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.sla.spi.ServiceLevelAgreementProvider#builder()
*/
@Override
public ServiceLevelAgreementBuilder builder() {
try {
Session session = getSession();
Node slasNode = session.getNode(SLA_PATH);
Node slaNode = slasNode.addNode("sla-" + UUID.randomUUID(), "tba:sla");
return builder(slaNode);
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to create an sla node", e);
}
}
public ServiceLevelAgreementBuilder builder(ServiceLevelAgreement.ID id) {
try {
Session session = getSession();
Node slaNode = session.getNodeByIdentifier(id.toString());
if (slaNode == null || !slaNode.isNodeType("tba:sla")) {
throw new MetadataRepositoryException("Failed to get sla node for " + id);
}
//clear out any obligations/metrics associated to this node as the builder will bring in new ones
JcrServiceLevelAgreement sla = new JcrServiceLevelAgreement(slaNode);
sla.clear();
return builder(slaNode);
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to create an sla node", e);
}
}
/**
* Returns a builder that constructs an SLA rooted by the given node. This method is exposed to support
* other JCR-based providers that may construct object that have embedded SLA's that are not managed by
* this provider.
*
* @param slaNode the root node of the SLA
* @return a builder to construct the sla
*/
public ServiceLevelAgreementBuilder builder(Node slaNode) throws RepositoryException {
return new SLABuilderImpl(slaNode);
}
@Override
public ID resolveId(Serializable ser) {
if (ser instanceof JcrServiceLevelAgreement.SlaId) {
return (JcrServiceLevelAgreement.SlaId) ser;
} else {
return new JcrServiceLevelAgreement.SlaId(ser);
}
}
public ServiceLevelAgreementCheckBuilder slaCheckBuilder(ServiceLevelAgreement.ID slaId) {
try {
Session session = getSession();
Node n = session.getNodeByIdentifier(slaId.toString());
return new SLACheckBuilderImpl(n);
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Unable to create slaCheckBuilder. Error attempting to find related SLA by id of " + slaId);
}
}
private static class ObligationBuilderImpl<B> implements ObligationBuilder<B> {
private Node obNode;
private SLABuilderImpl slaBuilder;
private ObligationGroupBuilderImpl groupBuilder;
private String description;
private Set<Metric> metrics = new HashSet<Metric>();
public ObligationBuilderImpl(Node node, SLABuilderImpl bldr) {
this.obNode = node;
this.slaBuilder = bldr;
}
public ObligationBuilderImpl(Node node, ObligationGroupBuilderImpl bldr) {
this.obNode = node;
this.groupBuilder = bldr;
}
@Override
public ObligationBuilder<B> description(String descr) {
this.description = descr;
return this;
}
@Override
public ObligationBuilder<B> metric(Metric metric, Metric... more) {
this.metrics.add(metric);
for (Metric another : more) {
this.metrics.add(another);
}
return this;
}
@Override
public ObligationBuilder<B> metric(Collection<Metric> metrics) {
this.metrics.addAll(metrics);
return this;
}
@Override
@SuppressWarnings("unchecked")
public B build() {
try {
JcrPropertyUtil.setProperty(this.obNode, "tba:description", this.description);
for (Metric metric : this.metrics) {
Node metricNode = this.obNode.addNode(JcrObligation.METRICS, JcrObligation.METRIC_TYPE);
JcrPropertyUtil.setProperty(metricNode, JcrObligation.NAME, metric.getClass().getSimpleName());
JcrPropertyUtil.setProperty(metricNode, JcrObligation.DESCRIPTION, metric.getDescription());
JcrUtil.addGenericJson(metricNode, JcrObligation.JSON, metric);
}
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to build the obligation", e);
}
if (this.groupBuilder != null) {
return (B) this.groupBuilder;
} else {
return (B) this.slaBuilder;
}
}
}
private static class ObligationGroupBuilderImpl implements ObligationGroupBuilder {
private Node groupNode;
private Condition condition;
private SLABuilderImpl slaBuilder;
public ObligationGroupBuilderImpl(Node node, Condition cond, SLABuilderImpl slaBuilder) {
this.groupNode = node;
this.condition = cond;
this.slaBuilder = slaBuilder;
}
@Override
public ObligationGroupBuilder obligation(Obligation obligation) {
// TODO Does not to work in the current JCR implementation. Perhaps it should not be supported at all in the builder.
throw new UnsupportedOperationException();
}
@Override
public ObligationBuilder<ObligationGroupBuilder> obligationBuilder() {
try {
Node obNode = this.groupNode.addNode(JcrObligationGroup.OBLIGATIONS, JcrObligationGroup.OBLIGATION_TYPE);
return new ObligationBuilderImpl<ObligationGroupBuilder>(obNode, this);
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to create the obligation group builder", e);
}
}
@Override
public ServiceLevelAgreementBuilder build() {
JcrPropertyUtil.setProperty(this.groupNode, JcrObligationGroup.CONDITION, this.condition);
return this.slaBuilder;
}
}
private static class SLACheckBuilderImpl implements ServiceLevelAgreementCheckBuilder {
private JcrServiceLevelAgreement sla;
private Node slaCheckNode;
private String cronExpression;
private List<ServiceLevelAgreementActionConfiguration> serviceLevelAgreementActionConfigurations;
public SLACheckBuilderImpl(Node slaNode) {
this.sla = new JcrServiceLevelAgreement(slaNode);
}
public SLACheckBuilderImpl removeSlaChecks() {
try {
Iterator<Node> nodesItr = (Iterator<Node>) this.sla.getNode().getNodes(JcrServiceLevelAgreement.SLA_CHECKS);
while (nodesItr != null && nodesItr.hasNext()) {
Node action = nodesItr.next();
action.remove();
}
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to remove the SLA Checks", e);
}
return this;
}
@Override
public ServiceLevelAgreementCheckBuilder actionConfiguration(ServiceLevelAgreementActionConfiguration configuration) {
if (this.serviceLevelAgreementActionConfigurations == null) {
this.serviceLevelAgreementActionConfigurations = new ArrayList<>();
}
if (configuration != null) {
this.serviceLevelAgreementActionConfigurations.add(configuration);
}
return this;
}
@Override
public ServiceLevelAgreementCheckBuilder actionConfigurations(List<ServiceLevelAgreementActionConfiguration> configurations) {
if (this.serviceLevelAgreementActionConfigurations == null) {
this.serviceLevelAgreementActionConfigurations = new ArrayList<>();
}
if (configurations != null) {
this.serviceLevelAgreementActionConfigurations.addAll(configurations);
}
return this;
}
@Override
public ServiceLevelAgreementCheckBuilder cronExpression(String cronExpression) {
this.cronExpression = cronExpression;
return this;
}
@Override
public ServiceLevelAgreementCheck build() {
try {
//create the Check Node
this.slaCheckNode = JcrUtil.getOrCreateNode(this.sla.getNode(), JcrServiceLevelAgreement.SLA_CHECKS, JcrServiceLevelAgreementCheck.NODE_TYPE);
String slaName = sla.getName();
JcrPropertyUtil.setProperty(slaCheckNode, JcrPropertyConstants.DESCRIPTION, "SLA Check for " + slaName + " using schedule " + cronExpression);
JcrPropertyUtil.setProperty(slaCheckNode, JcrPropertyConstants.TITLE, "SLA Check");
JcrPropertyUtil.setProperty(slaCheckNode, JcrServiceLevelAgreementCheck.CRON_SCHEDULE, cronExpression);
if (this.serviceLevelAgreementActionConfigurations != null && !this.serviceLevelAgreementActionConfigurations.isEmpty()) {
for (ServiceLevelAgreementActionConfiguration actionConfiguration : this.serviceLevelAgreementActionConfigurations) {
Node node = this.slaCheckNode.addNode(JcrServiceLevelAgreementCheck.ACTION_CONFIGURATIONS, JcrServiceLevelAgreementCheck.ACTION_CONFIGURATION_TYPE);
JcrPropertyUtil.setProperty(node, JcrPropertyConstants.TITLE, actionConfiguration.getClass().getSimpleName());
ServiceLevelAgreementActionConfig annotation = actionConfiguration.getClass().getAnnotation(ServiceLevelAgreementActionConfig.class);
String desc = actionConfiguration.getClass().getSimpleName();
if (annotation != null) {
desc = annotation.description();
}
JcrPropertyUtil.setProperty(node, JcrPropertyConstants.DESCRIPTION, desc);
JcrUtil.addGenericJson(node, JcrPropertyConstants.JSON, actionConfiguration);
}
}
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to build the SLA Check", e);
}
return new JcrServiceLevelAgreementCheck(this.slaCheckNode);
}
}
protected class SLABuilderImpl implements ServiceLevelAgreementBuilder {
private Node slaNode;
private String name;
private String description;
private List<? extends ServiceLevelAgreementActionConfiguration> serviceLevelAgreementActionConfigurations;
public SLABuilderImpl(Node node) throws RepositoryException {
this.slaNode = node;
}
@Override
public ServiceLevelAgreementBuilder name(String name) {
this.name = name;
return this;
}
@Override
public ServiceLevelAgreementBuilder description(String description) {
this.description = description;
return this;
}
@Override
public ServiceLevelAgreementBuilder obligation(Obligation obligation) {
// TODO This isn't going to work in the current JCR implementation. Perhaps it should not be supported at all in the builder.
throw new UnsupportedOperationException();
}
@Override
public ObligationBuilder<ServiceLevelAgreementBuilder> obligationBuilder() {
try {
Node groupNode = null;
if (this.slaNode.hasProperty(JcrServiceLevelAgreement.DEFAULT_GROUP)) {
groupNode = this.slaNode.getProperty(JcrServiceLevelAgreement.DEFAULT_GROUP).getNode();
} else {
groupNode = this.slaNode.addNode(JcrServiceLevelAgreement.GROUPS, JcrServiceLevelAgreement.GROUP_TYPE);
this.slaNode.setProperty(JcrServiceLevelAgreement.DEFAULT_GROUP, groupNode);
}
Node obNode = groupNode.addNode(JcrObligationGroup.OBLIGATIONS, JcrObligationGroup.OBLIGATION_TYPE);
return new ObligationBuilderImpl<ServiceLevelAgreementBuilder>(obNode, this);
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to build the obligation node", e);
}
}
@Override
public ObligationBuilder<ServiceLevelAgreementBuilder> obligationBuilder(Condition condition) {
try {
Node groupNode = this.slaNode.getProperty(JcrServiceLevelAgreement.DEFAULT_GROUP).getNode();
groupNode.setProperty(JcrObligationGroup.CONDITION, condition.name());
Node obNode = groupNode.addNode(JcrObligationGroup.OBLIGATIONS, JcrObligationGroup.OBLIGATION_TYPE);
return new ObligationBuilderImpl<ServiceLevelAgreementBuilder>(obNode, this);
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to build the obligation group node", e);
}
}
@Override
public ObligationGroupBuilder obligationGroupBuilder(Condition condition) {
try {
Node groupNode = this.slaNode.addNode(JcrServiceLevelAgreement.GROUPS, JcrServiceLevelAgreement.GROUP_TYPE);
return new ObligationGroupBuilderImpl(groupNode, condition, this);
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to build the obligation group node", e);
}
}
public ServiceLevelAgreementBuilder actionConfigurations(List<? extends ServiceLevelAgreementActionConfiguration> actionConfigurations) {
this.serviceLevelAgreementActionConfigurations = actionConfigurations;
return this;
}
@Override
public ServiceLevelAgreement build() {
JcrPropertyUtil.setProperty(this.slaNode, JcrServiceLevelAgreement.NAME, this.name);
JcrPropertyUtil.setProperty(this.slaNode, JcrServiceLevelAgreement.DESCRIPTION, this.description);
JcrServiceLevelAgreement agreement = new JcrServiceLevelAgreement(this.slaNode);
//always make it enabled by default
agreement.setEnabled(true);
return agreement;
}
}
}