/** * Copyright [2011] [Datasalt Systems S.L.] * * 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. */ package com.datasalt.utils.commons; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import com.google.common.base.Predicate; import com.google.common.base.Predicates; /** * A class for managing data repositories in Hadoop. A repository is a collection of packages. A package is the base * place where the execution of Hadoop pipeline relies on. All the outputs of each single Hadoop pipeline execution are * written to same package. * * Each execution of a Hadoop pipeline needs a different package. This class helps with the management of repositories, * by allowing creating new packages, and navigate over the existing ones. * * The package name format is [package_prefix]-yyyy-MM-dd-HH-mm-ss-[static counter]. Each package has an associated * status. * * @author ivan */ @SuppressWarnings({ "unchecked" }) public class RepoTool { Path basePath; FileSystem fs; String packagePrefix; SimpleDateFormat dateFormatter; // Counter for avoiding same name package in the same second static AtomicInteger count = new AtomicInteger(); public final static String PACKAGE_STATUS_FILE = "package.status"; public enum PackageStatus { NOT_DEFINED, FINISHED, FAILED } public FileSystem getFileSystem(){ return fs; } public RepoTool(Path basePath, String packagePrefix, FileSystem fs) { this.basePath = basePath; this.fs = fs; this.packagePrefix = packagePrefix; dateFormatter = new SimpleDateFormat("'" + packagePrefix + "'-" + "yyyy-MM-dd-HH-mm-ss"); } /** * Returns a new package using the current date. The new package is created and marked status NOT_DEFINED */ public Path newPackage() throws IOException { Path p = new Path(basePath, dateFormatter.format(new Date()) + "-" + String.format("%05d", count.incrementAndGet())); fs.mkdirs(p); setStatus(fs, p, PackageStatus.NOT_DEFINED); return p; } protected Path[] getPackages(Predicate<FileStatus> filter) throws IOException { FileStatus[] lfs = fs.listStatus(basePath); if(lfs == null) { return new Path[0]; } ArrayList<String> paths = new ArrayList<String>(); for(int i = 0; i < lfs.length; i++) { FileStatus status = lfs[i]; if(status.isDir()) { try { dateFormatter.parse(status.getPath().getName()); if(!filter.apply(status)) { paths.add(status.getPath().toString()); } } catch(ParseException e) { continue; } } } Collections.sort(paths, Collections.reverseOrder()); Path[] ret = new Path[paths.size()]; for(int i = 0; i < paths.size(); i++) { ret[i] = new Path(paths.get(i)); } return ret; } /** * Return the list of packages in this repository, sorted by date in reverse order. */ public Path[] getPackages() throws IOException { return getPackages(Predicates.<FileStatus> alwaysFalse()); } /** * Return the list of packages with the given status in this repository, sorted by date in reverse order. */ public Path[] getPackagesWithStatus(final Enum<?> status) throws IOException { return getPackages(new Predicate<FileStatus>() { @Override public boolean apply(FileStatus input) { try { return status != getStatus(fs, status.getClass(), input.getPath()); } catch(IOException e) { new RuntimeException("Exception when retriving status from the package " + input.getPath().toString(), e); return true; } } }); } /** * Return the newest package with the given status. Null if no package at all. */ public Path getNewestPackageWithStatus(Enum<?> status) throws IOException { Path[] list = getPackagesWithStatus(status); if(list != null && list.length > 0) { return getPackagesWithStatus(status)[0]; } else { return null; } } /** * Sets the given status for the given package. A file with name status.getClass().getSimpleName() is created in the * package folder to save the status. */ public static void setStatus(FileSystem fs, Path packagePath, Enum<?> status) throws IOException { HadoopUtils.stringToFile(fs, new Path(packagePath, status.getClass().getSimpleName()), status.toString()); } /** * Return the status for the given enumType in the given package. Null if not status is found. */ public static <T extends Enum<T>> T getStatus(FileSystem fs, Class<T> enumClass, Path packagePath) throws IOException { String status = HadoopUtils.fileToString(fs, new Path(packagePath, enumClass.getSimpleName())); if(status != null) { return Enum.valueOf(enumClass, status.trim()); } else { return null; } } }