och.service.props.impl.FileProps.java Source code

Java tutorial

Introduction

Here is the source code for och.service.props.impl.FileProps.java

Source

/*
 * Copyright 2015 Evgeny Dolganov (evgenij.dolganov@gmail.com).
 *
 * 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 och.service.props.impl;

import static java.util.Collections.*;
import static och.util.FileUtil.*;
import static och.util.MapUtil.*;
import static och.util.Util.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import och.util.concurrent.ExecutorsUtil;

import org.apache.commons.logging.Log;

public class FileProps extends BaseProps {

    private static Log log = getLog(FileProps.class);

    private static ScheduledExecutorService syncService;

    private static synchronized ScheduledExecutorService getExecutorService() {
        if (syncService == null) {
            syncService = ExecutorsUtil.newScheduledThreadPool("FileProps-sync", 1);
        }
        return syncService;
    }

    public static interface PropsModifier {
        Properties modify(Properties props);
    }

    public static FileProps createPropsWithoutUpdate(File propsFile) {
        return new FileProps(propsFile, 0, null);
    }

    public static List<FileProps> createFileProps(Collection<String> paths) {
        LinkedList<FileProps> list = new LinkedList<>();
        for (String path : paths) {
            File file = new File(path);
            if (!file.exists()) {
                log.error("can't find file by path: " + path);
                continue;
            }
            list.addFirst(new FileProps(file));
        }
        return list;
    }

    private final File file;
    private final PropsModifier propsModifier;
    private volatile long curModified;
    private volatile Properties state = new Properties();

    public FileProps(String propsPath) {
        this(new File(propsPath));
    }

    public FileProps(File propsFile) {
        this(propsFile, null);
    }

    public FileProps(File propsFile, PropsModifier propsModifier) {
        this(propsFile, 1000 * 60, propsModifier);
    }

    public FileProps(File propsFile, long updateTimeMs, PropsModifier propsModifier) {

        this.file = propsFile;
        this.propsModifier = propsModifier;

        updateFromFileIfNeed();

        updateTimeMs = updateTimeMs > 0 && updateTimeMs < 50 ? 50 : updateTimeMs;
        if (updateTimeMs > 0) {
            getExecutorService().scheduleWithFixedDelay(() -> updateFromFileIfNeed(), updateTimeMs, updateTimeMs,
                    TimeUnit.MILLISECONDS);
        }

    }

    public File getFile() {
        return file;
    }

    @Override
    public String getVal(Object oKey, String defaultVal) {
        String key = String.valueOf(oKey);
        Properties props = state;
        return props.containsKey(key) ? (String) props.get(key) : defaultVal;
    }

    @Override
    public void putVal(Object oKey, String val) {
        String key = String.valueOf(oKey);
        putObjVal(key, val);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public Map<String, String> toMap() {
        HashMap<String, String> out = new HashMap<>((Map) state);
        return out;
    }

    private synchronized void putObjVal(String key, String val) {

        Properties props = state;

        Properties newProps = new Properties();
        newProps.putAll(props);
        if (val == null)
            newProps.remove(key);
        else
            newProps.put(key, val);
        saveToFile(newProps);

        fireChangedEvent(singleton(key));
    }

    @SuppressWarnings("unchecked")
    public synchronized void updateFromFileIfNeed() {
        long lastModified = file.lastModified();
        if (curModified == lastModified)
            return;

        log.info("load props from " + file.getAbsolutePath());
        Properties props = null;
        try {

            FileInputStream is = new FileInputStream(file);
            Properties fromFile = new Properties();
            fromFile.load(new InputStreamReader(is, "UTF-8"));
            is.close();

            props = fromFile;

        }
        //empty props
        catch (FileNotFoundException e) {
            props = new Properties();
        }
        //invalid read
        catch (Exception e) {
            props = null;
            log.error("can't read props", e);
        }

        if (props == null)
            return;
        Properties newState = modifyProps(props);
        Properties oldState = state;

        this.curModified = lastModified;
        this.state = newState;

        Set<String> keys = getUpdatedKeys(oldState, newState);
        if (!isEmpty(keys)) {
            fireChangedEvent(keys);
        }
    }

    private void saveToFile(Properties newProps) {

        log.info("save props to " + file.getAbsolutePath());

        Properties newState = modifyProps(newProps);

        StringBuilder sb = new StringBuilder();
        sb.append("#Props from app");
        for (Entry<Object, Object> entry : newProps.entrySet()) {
            sb.append("\n" + entry.getKey() + "=" + entry.getValue());
        }

        try {
            replaceFileUTF8(file, sb.toString());
        } catch (Exception e) {
            log.error("can't saveToFile", e);
            return;
        }

        this.curModified = file.lastModified();
        this.state = newState;
    }

    public Properties modifyProps(Properties props) {
        PropsModifier curModifier = this.propsModifier;
        if (curModifier == null)
            return props;
        return curModifier.modify(props);
    }

    @Override
    public String toString() {
        return "FileProps [file=" + file + "]";
    }

}