/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.falcon.lifecycle.retention;
import org.apache.commons.lang3.StringUtils;
import org.apache.falcon.FalconException;
import org.apache.falcon.entity.FeedHelper;
import org.apache.falcon.entity.parser.ValidationException;
import org.apache.falcon.entity.v0.Frequency;
import org.apache.falcon.entity.v0.feed.Cluster;
import org.apache.falcon.entity.v0.feed.Feed;
import org.apache.falcon.entity.v0.feed.Property;
import org.apache.falcon.entity.v0.feed.RetentionStage;
import org.apache.falcon.entity.v0.feed.Sla;
import org.apache.falcon.expression.ExpressionHelper;
import org.apache.falcon.util.StartupProperties;
import java.util.Date;
/**
* Retention policy which deletes all instances of instance time older than a given time.
* It will create the workflow and coordinators for this policy.
*/
public class AgeBasedDelete extends RetentionPolicy {
public static final String LIMIT_PROPERTY_NAME = "retention.policy.agebaseddelete.limit";
@Override
public void validate(Feed feed, String clusterName) throws FalconException {
// validate that it is a valid cluster
Cluster cluster = FeedHelper.getCluster(feed, clusterName);
Frequency retentionLimit = getRetentionLimit(feed, clusterName);
if (cluster != null) {
validateLimitWithSla(feed, cluster, retentionLimit.toString());
validateLimitWithLateData(feed, cluster, retentionLimit.toString());
String lifecycleEngine = StartupProperties.get().getProperty("lifecycle.engine.impl",
"org.apache.falcon.lifecycle.engine.oozie.OoziePolicyBuilderFactory");
if ("org.apache.falcon.lifecycle.engine.oozie.OoziePolicyBuilderFactory".equals(lifecycleEngine)) {
validateRetentionFrequencyForOozie(feed, clusterName);
}
}
}
private void validateRetentionFrequencyForOozie(Feed feed, String clusterName) throws FalconException {
// retention shouldn't be more frequent than hours(1) for Oozie Builders.
Frequency retentionFrequency = FeedHelper.getLifecycleRetentionFrequency(feed, clusterName);
if (retentionFrequency.getTimeUnit() == Frequency.TimeUnit.minutes
&& retentionFrequency.getFrequencyAsInt() < 60) {
throw new ValidationException("Feed Retention can not be more frequent than hours(1)");
}
}
private void validateLimitWithLateData(Feed feed, Cluster cluster, String retention) throws FalconException {
ExpressionHelper evaluator = ExpressionHelper.get();
long retentionPeriod = evaluator.evaluate(retention, Long.class);
if (feed.getLateArrival() != null) {
String feedCutoff = feed.getLateArrival().getCutOff().toString();
long feedCutOffPeriod = evaluator.evaluate(feedCutoff, Long.class);
if (retentionPeriod < feedCutOffPeriod) {
throw new ValidationException(
"Feed's retention limit: " + retention + " of referenced cluster " + cluster.getName()
+ " should be more than feed's late arrival cut-off period: " + feedCutoff
+ " for feed: " + feed.getName());
}
}
}
private void validateLimitWithSla(Feed feed, Cluster cluster, String retentionExpression) throws FalconException {
// test that slaHigh is less than retention
Sla clusterSla = FeedHelper.getSLA(cluster, feed);
if (clusterSla != null) {
ExpressionHelper evaluator = ExpressionHelper.get();
ExpressionHelper.setReferenceDate(new Date());
Frequency slaHighExpression = clusterSla.getSlaHigh();
Date slaHigh = new Date(evaluator.evaluate(slaHighExpression.toString(), Long.class));
Date retention = new Date(evaluator.evaluate(retentionExpression, Long.class));
if (slaHigh.after(retention)) {
throw new ValidationException("slaHigh of Feed: " + slaHighExpression
+ " is greater than retention of the feed: " + retentionExpression
+ " for cluster: " + cluster.getName()
);
}
}
}
public Frequency getRetentionLimit(Feed feed, String clusterName) throws FalconException {
RetentionStage retention = FeedHelper.getRetentionStage(feed, clusterName);
if (retention != null) {
String limit = null;
for (Property property : retention.getProperties().getProperties()) {
if (StringUtils.equals(property.getName(), LIMIT_PROPERTY_NAME)) {
limit = property.getValue();
}
}
if (limit == null) {
throw new FalconException("Property: " + LIMIT_PROPERTY_NAME + " is required for "
+ getName() + " policy.");
}
try {
return new Frequency(limit);
} catch (IllegalArgumentException e) {
throw new FalconException("Invalid value for property: " + LIMIT_PROPERTY_NAME + ", should be a valid "
+ "frequency e.g. hours(2)", e);
}
} else {
throw new FalconException("Cluster " + clusterName + " doesn't contain retention stage");
}
}
}