001/*
002 * Stallion Core: A Modern Web Framework
003 *
004 * Copyright (C) 2015 - 2016 Stallion Software LLC.
005 *
006 * This program is free software: you can redistribute it and/or modify it under the terms of the
007 * GNU General Public License as published by the Free Software Foundation, either version 2 of
008 * the License, or (at your option) any later version. This program is distributed in the hope that
009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
011 * License for more details. You should have received a copy of the GNU General Public License
012 * along with this program.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
013 *
014 *
015 *
016 */
017
018package io.stallion.services;
019
020import io.stallion.Context;
021import org.apache.commons.codec.digest.DigestUtils;
022import org.apache.commons.io.FileUtils;
023import org.apache.commons.io.IOUtils;
024
025import java.io.File;
026import java.io.IOException;
027import java.security.MessageDigest;
028import java.util.Map;
029
030import static io.stallion.utils.Literals.*;
031
032/**
033 * A service that stores values semi-permanently both in the local file system
034 * and in memory.
035 *
036 * All values will live in memory as long as the process does, and in the file system as
037 * long as the cache is not cleaned.
038 *
039 * This should only be used for bounded data, otherwise memory could explode.
040 *
041 *
042 *
043 */
044public class PermaCache {
045    private static final Map<String, String> cache = map();
046    private static String cacheFolder;
047
048    public static String get(String key) {
049        if (cache.containsKey(key)) {
050            return cache.get(key);
051        }
052        if (!empty(getCacheFolder())) {
053            File file = new File(getCacheFolder() + "/" + DigestUtils.md5Hex(key));
054            if (file.exists()) {
055                try {
056                    String content = FileUtils.readFileToString(file, "utf-8");
057                    cache.put(key, content);
058                    return content;
059                } catch (IOException e) {
060                    Log.exception(e, "Error reading file from disk: " + file.toString());
061                }
062            }
063        }
064        return null;
065    }
066
067    public static void set(String key, String contents) {
068        cache.put(key, contents);
069        if (!empty(getCacheFolder())) {
070            File file = new File(getCacheFolder() + "/" + DigestUtils.md5Hex(key));
071            try {
072                FileUtils.write(file, contents, "utf-8");
073            } catch (IOException e) {
074                Log.exception(e, "Error writing file to cache: " + file.toString());
075            }
076        }
077    }
078
079    public static void setInMemoryOnly(String key, String contents) {
080        cache.put(key, contents);
081    }
082
083    public static String getCacheFolder() {
084        if (cacheFolder == null) {
085            cacheFolder = initCacheFolder();
086        }
087        return cacheFolder;
088    }
089
090    public static String initCacheFolder() {
091        File tmp = new File("/tmp/stallion-cache");
092        if (tmp.isDirectory() && tmp.canWrite()) {
093            return "/tmp/stallion-cache";
094        }
095        Boolean made = tmp.mkdirs();
096        if (made) {
097            return "/tmp/stallion-cache";
098        }
099        String appCachePath = Context.settings().getDataDirectory() + "/_cache";
100        tmp = new File(appCachePath);
101        if (tmp.isDirectory() && tmp.canWrite()) {
102            return appCachePath;
103        }
104        made = tmp.mkdirs();
105        if (made) {
106            return appCachePath;
107        }
108        Log.warn("Could not create cache folder /tmp/stallion-cache or {0}!", appCachePath);
109        return "";
110    }
111
112}