gobblin.runtime.spec_store.FSSpecStore.java Source code

Java tutorial

Introduction

Here is the source code for gobblin.runtime.spec_store.FSSpecStore.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.spec_store;

import java.io.IOException;
import java.net.URI;
import java.util.Collection;

import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
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 com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.typesafe.config.Config;

import gobblin.configuration.ConfigurationKeys;
import gobblin.runtime.api.GobblinInstanceEnvironment;
import gobblin.runtime.api.Spec;
import gobblin.runtime.api.SpecNotFoundException;
import gobblin.runtime.api.SpecSerDe;
import gobblin.runtime.api.SpecStore;
import gobblin.util.PathUtils;

/**
 * The Spec Store for file system to persist the Spec information.
 * Note:
 * 1. This implementation has no support for caching.
 * 2. This implementation does not performs implicit version management.
 *    For implicit version management, please use a wrapper FSSpecStore.
 */
public class FSSpecStore implements SpecStore {

    protected final Logger log;
    protected final Config sysConfig;
    protected final FileSystem fs;
    protected final String fsSpecStoreDir;
    protected final Path fsSpecStoreDirPath;
    protected final SpecSerDe specSerDe;

    public FSSpecStore(GobblinInstanceEnvironment env, SpecSerDe specSerDe) throws IOException {
        this(env.getSysConfig().getConfig(), specSerDe, Optional.<Logger>absent());
    }

    public FSSpecStore(Config sysConfig, SpecSerDe specSerDe) throws IOException {
        this(sysConfig, specSerDe, Optional.<Logger>absent());
    }

    public FSSpecStore(GobblinInstanceEnvironment env, SpecSerDe specSerDe, Optional<Logger> log)
            throws IOException {
        this(env.getSysConfig().getConfig(), specSerDe, log);
    }

    public FSSpecStore(Config sysConfig, SpecSerDe specSerDe, Optional<Logger> log) throws IOException {
        Preconditions.checkArgument(sysConfig.hasPath(ConfigurationKeys.SPECSTORE_FS_DIR_KEY),
                "FS SpecStore path must be specified.");

        this.log = log.isPresent() ? log.get() : LoggerFactory.getLogger(getClass());
        this.sysConfig = sysConfig;
        this.specSerDe = specSerDe;
        this.fsSpecStoreDir = this.sysConfig.getString(ConfigurationKeys.SPECSTORE_FS_DIR_KEY);
        this.fsSpecStoreDirPath = new Path(this.fsSpecStoreDir);
        this.log.info("FSSpecStore directory is: " + this.fsSpecStoreDir);
        try {
            this.fs = this.fsSpecStoreDirPath.getFileSystem(new Configuration());
        } catch (IOException e) {
            throw new RuntimeException("Unable to detect job config directory file system: " + e, e);
        }
        if (!this.fs.exists(this.fsSpecStoreDirPath)) {
            this.log.info("FSSpecStore directory: " + this.fsSpecStoreDir + " did not exist. Creating it.");
            this.fs.mkdirs(this.fsSpecStoreDirPath);
        }
    }

    @Override
    public boolean exists(URI specUri) throws IOException {
        Preconditions.checkArgument(null != specUri, "Spec URI should not be null");

        FileStatus[] fileStatuses = fs.listStatus(this.fsSpecStoreDirPath);
        for (FileStatus fileStatus : fileStatuses) {
            if (StringUtils.startsWith(fileStatus.getPath().getName(), specUri.toString())) {
                return true;
            }
        }

        return false;
    }

    @Override
    public void addSpec(Spec spec) throws IOException {
        Preconditions.checkArgument(null != spec, "Spec should not be null");

        log.info(String.format("Adding Spec with URI: %s in FSSpecStore: %s", spec.getUri(),
                this.fsSpecStoreDirPath));
        Path specPath = getPathForURI(this.fsSpecStoreDirPath, spec.getUri(), spec.getVersion());
        writeSpecToFile(specPath, spec);
    }

    @Override
    public boolean deleteSpec(Spec spec) throws IOException {
        Preconditions.checkArgument(null != spec, "Spec should not be null");

        return deleteSpec(spec.getUri(), spec.getVersion());
    }

    @Override
    public boolean deleteSpec(URI specUri) throws IOException {
        Preconditions.checkArgument(null != specUri, "Spec URI should not be null");

        try {
            return deleteSpec(specUri, getSpec(specUri).getVersion());
        } catch (SpecNotFoundException e) {
            throw new IOException(String.format("Issue in removing Spec: %s", specUri), e);
        }
    }

    @Override
    public boolean deleteSpec(URI specUri, String version) throws IOException {
        Preconditions.checkArgument(null != specUri, "Spec URI should not be null");
        Preconditions.checkArgument(null != version, "Version should not be null");

        try {
            log.info(String.format("Deleting Spec with URI: %s in FSSpecStore: %s", specUri,
                    this.fsSpecStoreDirPath));
            Path specPath = getPathForURI(this.fsSpecStoreDirPath, specUri, version);

            if (fs.exists(specPath)) {
                return fs.delete(specPath, false);
            } else {
                log.warn("No file with URI:" + specUri + " is found. Deletion failed.");
                return false;
            }
        } catch (IOException e) {
            throw new IOException(String.format("Issue in removing Spec: %s for Version: %s", specUri, version), e);
        }
    }

    @Override
    public Spec updateSpec(Spec spec) throws IOException, SpecNotFoundException {
        Preconditions.checkArgument(null != spec, "Spec should not be null");

        log.info(String.format("Updating Spec with URI: %s in FSSpecStore: %s", spec.getUri(),
                this.fsSpecStoreDirPath));
        Path specPath = getPathForURI(this.fsSpecStoreDirPath, spec.getUri(), spec.getVersion());
        writeSpecToFile(specPath, spec);

        return spec;
    }

    @Override
    public Spec getSpec(URI specUri) throws IOException, SpecNotFoundException {
        Preconditions.checkArgument(null != specUri, "Spec URI should not be null");

        Collection<Spec> specs = getAllVersionsOfSpec(specUri);
        Spec highestVersionSpec = null;

        for (Spec spec : specs) {
            if (null == highestVersionSpec) {
                highestVersionSpec = spec;
            } else if (null != spec.getVersion() && spec.getVersion().compareTo(spec.getVersion()) > 0) {
                highestVersionSpec = spec;
            }
        }

        if (null == highestVersionSpec) {
            throw new SpecNotFoundException(specUri);
        }

        return highestVersionSpec;
    }

    @Override
    public Spec getSpec(URI specUri, String version) throws IOException, SpecNotFoundException {
        Preconditions.checkArgument(null != specUri, "Spec URI should not be null");
        Preconditions.checkArgument(null != version, "Version should not be null");

        Path specPath = getPathForURI(this.fsSpecStoreDirPath, specUri, version);

        if (!fs.exists(specPath)) {
            throw new SpecNotFoundException(specUri);
        }

        return readSpecFromFile(specPath);
    }

    @Override
    public Collection<Spec> getAllVersionsOfSpec(URI specUri) throws IOException, SpecNotFoundException {
        Preconditions.checkArgument(null != specUri, "Spec URI should not be null");

        Collection<Spec> specs = getSpecs();
        Collection<Spec> filteredSpecs = Lists.newArrayList();
        for (Spec spec : specs) {
            if (spec.getUri().equals(specUri)) {
                filteredSpecs.add(spec);
            }
        }

        if (filteredSpecs.size() == 0) {
            throw new SpecNotFoundException(specUri);
        }

        return filteredSpecs;
    }

    @Override
    public Collection<Spec> getSpecs() throws IOException {
        Collection<Spec> specs = Lists.newArrayList();
        try {
            getSpecs(this.fsSpecStoreDirPath, specs);
        } catch (Exception e) {
            throw new IOException(e);
        }

        return specs;
    }

    private void getSpecs(Path directory, Collection<Spec> specs) throws IOException {
        FileStatus[] fileStatuses = fs.listStatus(directory);
        for (FileStatus fileStatus : fileStatuses) {
            if (fileStatus.isDirectory()) {
                getSpecs(fileStatus.getPath(), specs);
            } else {
                specs.add(readSpecFromFile(fileStatus.getPath()));
            }
        }
    }

    /***
     * Read and deserialized Spec from a file.
     * @param path File containing serialized Spec.
     * @return Spec
     * @throws IOException
     */
    protected Spec readSpecFromFile(Path path) throws IOException {
        Spec spec = null;

        try (FSDataInputStream fis = fs.open(path);) {
            spec = this.specSerDe.deserialize(IOUtils.toByteArray(fis));
        }

        return spec;
    }

    /***
     * Serialize and write Spec to a file.
     * @param specPath Spec file name.
     * @param spec Spec object to write.
     * @throws IOException
     */
    protected void writeSpecToFile(Path specPath, Spec spec) throws IOException {
        if (fs.exists(specPath)) {
            fs.delete(specPath, true);
        }

        byte[] serializedSpec = this.specSerDe.serialize(spec);
        try (FSDataOutputStream os = fs.create(specPath)) {
            os.write(serializedSpec);
        }
    }

    /**
     *
     * @param fsSpecStoreDirPath The directory path for specs.
     * @param uri Uri as the identifier of JobSpec
     * @return
     */
    protected Path getPathForURI(Path fsSpecStoreDirPath, URI uri, String version) {
        return PathUtils.addExtension(PathUtils.mergePaths(fsSpecStoreDirPath, new Path(uri)), version);
    }
}