com.indeed.imhotep.iql.cache.HDFSQueryCache.java Source code

Java tutorial

Introduction

Here is the source code for com.indeed.imhotep.iql.cache.HDFSQueryCache.java

Source

/*
 * Copyright (C) 2014 Indeed Inc.
 *
 * 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.indeed.imhotep.iql.cache;

import com.google.common.io.ByteStreams;
import com.indeed.imhotep.web.KerberosUtils;

import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.log4j.Logger;
import org.springframework.core.env.PropertyResolver;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @author vladimir
 */

public class HDFSQueryCache implements QueryCache {
    static final Logger log = Logger.getLogger(HDFSQueryCache.class);

    private boolean enabled;
    private FileSystem hdfs;
    private Path cachePath;
    private boolean cacheDirWorldWritable;

    public HDFSQueryCache(PropertyResolver props) {

        enabled = true;
        try {
            kerberosLogin(props);

            cachePath = new Path(props.getProperty("query.cache.hdfs.path"));
            hdfs = cachePath.getFileSystem(new org.apache.hadoop.conf.Configuration());
            cacheDirWorldWritable = props.getProperty("query.cache.worldwritable", Boolean.class);

            makeSurePathExists(cachePath);
        } catch (Exception e) {
            log.info("Failed to initialize the HDFS query cache. Caching disabled.", e);
            enabled = false;
        }
    }

    private void kerberosLogin(PropertyResolver props) {
        try {
            KerberosUtils.loginFromKeytab(props.getProperty("kerberos.principal"),
                    props.getProperty("kerberos.keytab"));
        } catch (IOException e) {
            log.error("Failed to log in to Kerberos", e);
        }
    }

    /**
     * Returns whether the HDFS cache is available.
     */
    @Override
    public boolean isEnabled() {
        return enabled;
    }

    /**
     * Returns whether the cache was requested to be enabled in the app config.
     */
    @Override
    public boolean isEnabledInConfig() {
        return true;
    }

    @Override
    public boolean isFileCached(String fileName) {
        if (!enabled) {
            return false;
        }
        try {
            final Path f = new Path(cachePath, fileName);
            return hdfs.exists(f);
        } catch (Exception e) {
            return false;
        }

    }

    @Override
    public InputStream getInputStream(String cachedFileName) throws IOException {
        if (!enabled) {
            throw new IllegalStateException("Can't send data from HDFS cache as it is disabled");
        }
        final Path f = new Path(cachePath, cachedFileName);
        return hdfs.open(f);
    }

    @Override
    public OutputStream getOutputStream(String cachedFileName) throws IOException {
        if (!enabled) {
            throw new IllegalStateException("Can't send data to HDFS cache as it is disabled");
        }
        makeSurePathExists(cachePath);
        final Path filePath = new Path(cachePath, cachedFileName);
        final Path tempPath = new Path(filePath.toString() + "." + (System.currentTimeMillis() % 100000) + ".tmp");

        final FSDataOutputStream fileOut = hdfs.create(tempPath);
        // Wrap the returned OutputStream so that we can finish when it is closed
        return new OutputStream() {
            private boolean closed = false;

            @Override
            public void write(byte[] b) throws IOException {
                fileOut.write(b);
            }

            @Override
            public void write(byte[] b, int off, int len) throws IOException {
                fileOut.write(b, off, len);
            }

            @Override
            public void flush() throws IOException {
                fileOut.flush();
            }

            @Override
            public void write(int b) throws IOException {
                fileOut.write(b);
            }

            @Override
            public void close() throws IOException {
                if (closed) {
                    return;
                }
                closed = true;
                fileOut.close();

                // Move to the final file location
                hdfs.rename(tempPath, filePath);
            }
        };
    }

    @Override
    public void writeFromFile(String cachedFileName, File localFile) throws IOException {
        final OutputStream cacheStream = getOutputStream(cachedFileName);
        final InputStream fileIn = new BufferedInputStream(new FileInputStream(localFile));
        ByteStreams.copy(fileIn, cacheStream);
        fileIn.close();
        cacheStream.close();
    }

    private void makeSurePathExists(Path path) throws IOException {
        if (!hdfs.exists(path)) {
            hdfs.mkdirs(cachePath);
            if (cacheDirWorldWritable) {
                hdfs.setPermission(path, FsPermission.valueOf("-rwxrwxrwx"));
            }
        }
    }

    /**
     * Tries to see if the HDFS is accessible by checking if the root cache dir exists and recreating if necessary.
     * @throws IOException
     */
    @Override
    public void healthcheck() throws IOException {
        if (!enabled) {
            throw new IllegalStateException("HDFS cache is not available");
        }

        makeSurePathExists(cachePath);
    }
}