/**
* 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.cleanup;
import org.apache.commons.el.ExpressionEvaluatorImpl;
import org.apache.falcon.FalconException;
import org.apache.falcon.entity.ClusterHelper;
import org.apache.falcon.entity.EntityUtil;
import org.apache.falcon.entity.store.ConfigurationStore;
import org.apache.falcon.entity.v0.Entity;
import org.apache.falcon.entity.v0.Frequency;
import org.apache.falcon.entity.v0.Frequency.TimeUnit;
import org.apache.falcon.entity.v0.cluster.Cluster;
import org.apache.falcon.expression.ExpressionHelper;
import org.apache.falcon.hadoop.HadoopClientFactory;
import org.apache.falcon.util.RuntimeProperties;
import org.apache.falcon.util.StartupProperties;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.jsp.el.ELException;
import javax.servlet.jsp.el.ExpressionEvaluator;
import java.io.IOException;
/**
* Falcon cleanup handler for cleaning up work, temp and log files
* left behind by falcon.
*/
public abstract class AbstractCleanupHandler {
protected static final Logger LOG = LoggerFactory.getLogger(AbstractCleanupHandler.class);
protected static final ConfigurationStore STORE = ConfigurationStore.get();
public static final ExpressionEvaluator EVALUATOR = new ExpressionEvaluatorImpl();
public static final ExpressionHelper RESOLVER = ExpressionHelper.get();
protected long getRetention(Entity entity, TimeUnit timeUnit)
throws FalconException {
String retention = getRetentionValue(timeUnit);
try {
return (Long) EVALUATOR.evaluate("${" + retention + "}",
Long.class, RESOLVER, RESOLVER);
} catch (ELException e) {
throw new FalconException("Unable to evalue retention limit: "
+ retention + " for entity: " + entity.getName());
}
}
private String getRetentionValue(Frequency.TimeUnit timeunit) {
return RuntimeProperties.get().getProperty(
"log.cleanup.frequency." + timeunit + ".retention", "days(3)");
}
protected FileStatus[] getAllLogs(org.apache.falcon.entity.v0.cluster.Cluster cluster,
Entity entity) throws FalconException {
FileSystem fs = getFileSystem(cluster);
FileStatus[] paths;
try {
Path logPath = getLogPath(cluster, entity);
paths = fs.globStatus(logPath);
} catch (IOException e) {
throw new FalconException(e);
}
return paths;
}
private Path getLogPath(Cluster cluster, Entity entity) {
// logsPath = base log path + relative path
return new Path(EntityUtil.getLogPath(cluster, entity), getRelativeLogPath());
}
protected FileSystem getFileSystem(org.apache.falcon.entity.v0.cluster.Cluster cluster)
throws FalconException {
return HadoopClientFactory.get().createFileSystem(ClusterHelper.getConfiguration(cluster));
}
protected void delete(Cluster cluster, Entity entity, long retention)
throws FalconException {
FileStatus[] logs = getAllLogs(cluster, entity);
delete(cluster, entity, retention, logs);
}
protected void delete(Cluster cluster, Entity entity, long retention, FileStatus[] logs)
throws FalconException {
if (logs == null || logs.length == 0) {
LOG.info("Nothing to delete for cluster: {}, entity: {}", cluster.getName(), entity.getName());
return;
}
long now = System.currentTimeMillis();
for (FileStatus log : logs) {
if (now - log.getModificationTime() > retention) {
try {
boolean isDeleted = getFileSystem(cluster).delete(log.getPath(), true);
if (!isDeleted) {
LOG.error("Unable to delete path: {}", log.getPath());
} else {
LOG.info("Deleted path: {}", log.getPath());
}
deleteParentIfEmpty(getFileSystem(cluster), log.getPath().getParent());
} catch (IOException e) {
throw new FalconException(" Unable to delete log file : "
+ log.getPath() + " for entity " + entity.getName()
+ " for cluster: " + cluster.getName(), e);
}
} else {
LOG.info("Retention limit: {} is less than modification {} for path: {}", retention,
(now - log.getModificationTime()), log.getPath());
}
}
}
private void deleteParentIfEmpty(FileSystem fs, Path parent) throws IOException {
FileStatus[] files = fs.listStatus(parent);
if (files != null && files.length == 0) {
LOG.info("Parent path: {} is empty, deleting path", parent);
fs.delete(parent, true);
deleteParentIfEmpty(fs, parent.getParent());
}
}
public abstract void cleanup() throws FalconException;
protected abstract String getRelativeLogPath();
protected String getCurrentColo() {
return StartupProperties.get().getProperty("current.colo", "default");
}
}