gobblin.runtime.locks.FileBasedJobLockFactory.java Source code

Java tutorial

Introduction

Here is the source code for gobblin.runtime.locks.FileBasedJobLockFactory.java

Source

/*
 * 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 gobblin.runtime.locks;

import java.io.IOException;
import java.net.URI;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.TimeoutException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.fs.permission.FsPermission;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;

import gobblin.configuration.ConfigurationKeys;
import gobblin.runtime.api.JobSpec;
import gobblin.util.HadoopUtils;

/**
 * A factory for file-based job locks. All locks are presented as files under a common directory.
 * If the directory does not exist, it will be automatically created and removed on close().
 */
public class FileBasedJobLockFactory implements JobLockFactory<FileBasedJobLock> {

    /** The URI of the file system with the directory for lock files*/
    public static final String FS_URI_CONFIG = "fsURI";
    /** The path to the directory for lock files*/
    public static final String LOCK_DIR_CONFIG = "lockDir";

    static final String DEFAULT_LOCK_DIR_PREFIX = "/tmp/gobblin-job-locks-";

    /**
     * Default waiting period (5 minutes).
     * TODO add configuration support
     */
    static final long DEFAULT_WAIT_MS = 300000;

    private final FileSystem fs;
    private final Path lockFileDir;
    private final Logger log;
    private final boolean deleteLockDirOnClose;

    /** Constructs a new factory
     * @throws IOException */
    public FileBasedJobLockFactory(FileSystem fs, String lockFileDir, Optional<Logger> log) throws IOException {
        this.fs = fs;
        this.lockFileDir = new Path(lockFileDir);
        this.log = log.or(LoggerFactory.getLogger(getClass().getName() + "-" + lockFileDir));
        this.deleteLockDirOnClose = !this.fs.exists(this.lockFileDir);
        if (deleteLockDirOnClose) {
            createLockDir(this.fs, this.lockFileDir);
        }
    }

    public FileBasedJobLockFactory(FileSystem fs, String lockFileDir) throws IOException {
        this(fs, lockFileDir, Optional.<Logger>absent());
    }

    /** Create a new instance using the specified factory and hadoop configurations. */
    public static FileBasedJobLockFactory create(Config factoryConfig, Configuration hadoopConf,
            Optional<Logger> log) throws IOException {
        FileSystem fs = factoryConfig.hasPath(FS_URI_CONFIG)
                ? FileSystem.get(URI.create(factoryConfig.getString(FS_URI_CONFIG)), hadoopConf)
                : getDefaultFileSystem(hadoopConf);
        String lockFilesDir = factoryConfig.hasPath(LOCK_DIR_CONFIG) ? factoryConfig.getString(LOCK_DIR_CONFIG)
                : getDefaultLockDir(fs, log);
        return new FileBasedJobLockFactory(fs, lockFilesDir, log);
    }

    public static FileSystem getDefaultFileSystem(Configuration hadoopConf) throws IOException {
        return FileSystem.getLocal(hadoopConf);
    }

    public static String getDefaultLockDir(FileSystem fs, Optional<Logger> log) {
        Random rng = new Random();
        Path dirName;
        try {
            do {
                dirName = new Path(DEFAULT_LOCK_DIR_PREFIX + rng.nextLong());
            } while (fs.exists(dirName));
        } catch (IllegalArgumentException | IOException e) {
            throw new RuntimeException("Unable to create job lock directory: " + e, e);
        }
        if (log.isPresent()) {
            log.get().info("Created default job lock directory: " + dirName);
        }
        return dirName.toString();
    }

    protected static void createLockDir(FileSystem fs, Path dirName) throws IOException {
        if (!fs.mkdirs(dirName, getDefaultDirPermissions())) {
            throw new RuntimeException("Unable to create job lock directory: " + dirName);
        }
    }

    protected static FsPermission getDefaultDirPermissions() {
        return new FsPermission(FsAction.ALL, FsAction.READ_EXECUTE, FsAction.NONE);
    }

    Path getLockFile(String jobName) {
        return new Path(lockFileDir, jobName + FileBasedJobLock.LOCK_FILE_EXTENSION);
    }

    /**
     * Acquire the lock.
     *
     * @throws JobLockException thrown if the {@link JobLock} fails to be acquired
     */
    void lock(Path lockFile) throws JobLockException {
        log.debug("Creating lock: {}", lockFile);
        try {
            if (!this.fs.createNewFile(lockFile)) {
                throw new JobLockException("Failed to create lock file " + lockFile.getName());
            }
        } catch (IOException e) {
            throw new JobLockException(e);
        }
    }

    /**
     * Release the lock.
     *
     * @throws JobLockException thrown if the {@link JobLock} fails to be released
     */
    void unlock(Path lockFile) throws JobLockException {
        log.debug("Removing lock: {}", lockFile);
        if (!isLocked(lockFile)) {
            return;
        }

        try {
            this.fs.delete(lockFile, false);
        } catch (IOException e) {
            throw new JobLockException(e);
        }
    }

    /**
     * Try locking the lock.
     *
     * @return <em>true</em> if the lock is successfully locked,
     *         <em>false</em> if otherwise.
     * @throws JobLockException thrown if the {@link JobLock} fails to be acquired
     */
    boolean tryLock(Path lockFile) throws JobLockException {
        log.debug("Attempting lock: {}", lockFile);
        try {
            return this.fs.createNewFile(lockFile);
        } catch (IOException e) {
            throw new JobLockException(e);
        }
    }

    /**
     * Check if the lock is locked.
     *
     * @return if the lock is locked
     * @throws JobLockException thrown if checking the status of the {@link JobLock} fails
     */
    boolean isLocked(Path lockFile) throws JobLockException {
        try {
            return this.fs.exists(lockFile);
        } catch (IOException e) {
            throw new JobLockException(e);
        }
    }

    public static Config getConfigForProperties(Properties properties) {
        return ConfigFactory.parseMap(ImmutableMap.<String, Object>builder()
                .put(FS_URI_CONFIG,
                        properties.getProperty(ConfigurationKeys.FS_URI_KEY, ConfigurationKeys.LOCAL_FS_URI))
                .put(LOCK_DIR_CONFIG, properties.getProperty(FileBasedJobLock.JOB_LOCK_DIR)).build());
    }

    public static FileBasedJobLockFactory createForProperties(Properties properties) throws JobLockException {
        try {
            FileSystem fs = FileSystem.get(
                    URI.create(
                            properties.getProperty(ConfigurationKeys.FS_URI_KEY, ConfigurationKeys.LOCAL_FS_URI)),
                    HadoopUtils.getConfFromProperties(properties));
            String lockFileDir = properties.getProperty(FileBasedJobLock.JOB_LOCK_DIR);
            return new FileBasedJobLockFactory(fs, lockFileDir);
        } catch (IOException e) {
            throw new JobLockException(e);
        }
    }

    @Override
    public FileBasedJobLock getJobLock(JobSpec jobSpec) throws TimeoutException {
        String jobName = getJobName(jobSpec);
        return new FileBasedJobLock(jobName, this);
    }

    @VisibleForTesting
    static String getJobName(JobSpec jobSpec) {
        return jobSpec.getUri().toString().replaceAll("[/.:]", "_");
    }

    @VisibleForTesting
    FileSystem getFs() {
        return fs;
    }

    @VisibleForTesting
    Path getLockFileDir() {
        return lockFileDir;
    }

    @Override
    public void close() throws IOException {
        if (this.deleteLockDirOnClose) {
            this.log.info("Delete auto-created lock directory: {}", getLockFileDir());
            if (!this.fs.delete(getLockFileDir(), true)) {
                this.log.warn("Failed to delete lock directory: {}", getLockFileDir());
            }
        }
    }

}