All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.vespa.config.server.zookeeper.ZKApplicationFile Maven / Gradle / Ivy

There is a newer version: 8.441.21
Show newest version
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.zookeeper;

import com.yahoo.json.Jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yahoo.config.application.api.ApplicationFile;
import com.yahoo.io.IOUtils;
import com.yahoo.path.Path;
import com.yahoo.text.Utf8;
import com.yahoo.vespa.config.util.ConfigUtils;

import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import static com.yahoo.vespa.config.server.zookeeper.ZKApplication.USERAPP_ZK_SUBPATH;
import static com.yahoo.yolean.Exceptions.uncheck;

/**
 * @author Ulf Lilleengen
 * @author Vegard Havdal
 */
class ZKApplicationFile extends ApplicationFile {

    private static final Logger log = Logger.getLogger("ZKApplicationFile");
    private final ZKApplication zkApp;
    private final ObjectMapper mapper = Jackson.mapper();

    public ZKApplicationFile(Path path, ZKApplication app) {
        super(path);
        this.zkApp = app;
    }

    @Override
    public boolean isDirectory() {
        Path zkPath = getZKPath(path);
        if (zkApp.exists(zkPath)) {
            String data = zkApp.getData(zkPath);
            return data == null || data.isEmpty() || ! zkApp.getChildren(zkPath).isEmpty();
        }
        return false;
    }

    @Override
    public boolean exists() {
        try {
            Path zkPath = getZKPath(path);
            return zkApp.exists(zkPath);
        } catch (RuntimeException e) {
            return false;
        }
    }

    @Override
    public ApplicationFile delete() {
        if (!listFiles().isEmpty()) {
            throw new RuntimeException("Can't delete, directory not empty: " + this);
        }
        zkApp.deleteRecurse(getZKPath(path));
        writeMetaFile(ContentStatusDeleted);
        return this;
    }

    @Override
    public Reader createReader() throws FileNotFoundException {
        Path zkPath = getZKPath(path);
        if ( ! zkApp.exists(zkPath)) throw new FileNotFoundException("No such path: " + path);

        return new StringReader(zkApp.getData(zkPath));
    }

    @Override
    public InputStream createInputStream() throws FileNotFoundException {
        Path zkPath = getZKPath(path);
        if ( ! zkApp.exists(zkPath)) throw new FileNotFoundException("No such path: " + path);

        return new ByteArrayInputStream(zkApp.getBytes(zkPath));
    }

    @Override
    public ApplicationFile createDirectory() {
        Path zkPath = getZKPath(path);
        if (isDirectory()) return this;
        if (exists()) {
            throw new IllegalArgumentException("Unable to create directory, file exists: " + path);
        }
        zkApp.create(zkPath);
        writeMetaFile(ContentStatusNew);
        return this;
    }

    @Override
    public ApplicationFile writeFile(Reader input) {
        return uncheck(() -> writeFile(Utf8.toBytes(IOUtils.readAll(input))));
    }

    @Override
    public ApplicationFile writeFile(InputStream input) {
        return uncheck(() -> writeFile(input.readAllBytes()));
    }

    private ApplicationFile writeFile(byte[] data) {
        Path zkPath = getZKPath(path);
        String status = ContentStatusNew;
        if (zkApp.exists(zkPath)) {
            status = ContentStatusChanged;
        }
        zkApp.putData(zkPath, data);
        writeMetaFile(data, status);
        return this;
    }

    @Override
    public ApplicationFile appendFile(String value) {
        Path zkPath = getZKPath(path);
        String status = ContentStatusNew;
        if (zkApp.exists(zkPath)) {
            status = ContentStatusChanged;
        }
        String existingData = zkApp.getData(zkPath);
        if (existingData == null)
            existingData = "";
        zkApp.putData(zkPath, Utf8.toBytes(existingData + value));
        writeMetaFile(value, status);
        return this;
    }

    @Override
    public List listFiles(PathFilter filter) {
        Path userPath = getZKPath(path);
        List ret = new ArrayList<>();
        for (String zkChild : zkApp.getChildren(userPath)) {
            Path childPath = path.append(zkChild);
            // Ignore dot-files.
            if (!childPath.getName().startsWith(".") && filter.accept(childPath)) {
                ret.add(new ZKApplicationFile(childPath, zkApp));
            }
        }
        return ret;
    }

    private static Path getZKPath(Path path) {
        if (path.isRoot()) {
            return Path.fromString(USERAPP_ZK_SUBPATH);
        }
        return Path.fromString(USERAPP_ZK_SUBPATH).append(path);
    }

    private void writeMetaFile(String status) {
        writeMetaFile((byte[]) null, status);
    }

    private void writeMetaFile(String input, String status) {
        writeMetaFile(Utf8.toBytes(input), status);
    }

    private void writeMetaFile(byte[] input, String status) {
        Path metaPath = getZKPath(getMetaPath());
        StringWriter writer = new StringWriter();
        try {
            mapper.writeValue(writer, new MetaData(status, input == null ? "" : ConfigUtils.getMd5(input)));
            log.log(Level.FINE, () -> "Writing meta file to " + metaPath);
            zkApp.putData(metaPath, writer.toString());
        } catch (IOException e) {
            throw new RuntimeException("Error writing meta file to " + metaPath, e);
        }
    }

    public MetaData getMetaData() {
        Path metaPath = getZKPath(getMetaPath());
        log.log(Level.FINE, () -> "Getting metadata for " + metaPath);
        if (!zkApp.exists(getZKPath(path))) {
            if (zkApp.exists(metaPath)) {
                return getMetaDataFromZk(metaPath);
            } else {
                return null;
            }
        }
        if (zkApp.exists(metaPath)) {
            return getMetaDataFromZk(metaPath);
        }
        return new MetaData(ContentStatusNew, isDirectory() ? "" : ConfigUtils.getMd5(zkApp.getData(getZKPath(path))));
    }

    private MetaData getMetaDataFromZk(Path metaPath) {
        try {
            return mapper.readValue(zkApp.getBytes(metaPath), MetaData.class);
        } catch (IOException e) {
            return null;
        }
    }

    @Override public long getSize() { return zkApp.getSize(getZKPath(path)); }

    @Override
    public int compareTo(ApplicationFile other) {
        if (other == this) return 0;
        return this.getPath().getName().compareTo((other).getPath().getName());
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy