/** * */ package logbook.scripting; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import logbook.constants.AppConstants; import logbook.internal.LoggerHolder; /** * スクリプトデータ永続化 * @author Nekopanda */ public class ScriptData { /** ロガー */ private static final LoggerHolder LOG = new LoggerHolder("script"); private static class DataObject implements Serializable { private static final long serialVersionUID = -115021202763582793L; public Date lastAccessed = null; public Object data; public transient boolean persist; public DataObject(Object data, boolean persist) { this.data = data; this.persist = persist; } @Override public String toString() { return this.data.toString(); } } private static Map<String, DataObject> dataMap = new HashMap<>(); private static boolean modified = false; // 始めてアクセスがあった時に読み込む static { try { load(); // 60日間アクセスがないデータを削除 cleanup(60); } catch (IOException e) { LOG.get().warn("スクリプトデータ読み込みに失敗しました", e); } } /** * 指定されたキーでデータを格納 * * setData(key, value, true)と同じです * @param key データのキー * @param value データ */ public static void setData(String key, Object value) { setData(key, value, true); } /** * 指定されたキーでデータを格納 * * データを保存する場合は、はシリアライズ可能でなければなりません。 * スクリプトのオブジェクト(連想配列や配列など)はシリアライズできないため保存できません。 * オブジェクトを保存したい場合はJSON.stringify()であらかじめシリアライズしておいてください。 * (復元はJSON.parse()でできます) * @param key データのキー * @param value データ * @param persist データを保存するかどうか */ public static void setData(String key, Object value, boolean persist) { DataObject data = dataMap.get(key); // lastAccessedはstore()で書き込まれる if (data != null) { data.data = value; data.lastAccessed = null; data.persist = persist; } else { dataMap.put(key, new DataObject(value, persist)); } if (persist) { modified = true; } } /** * 指定されたキーでデータを取得 * @param key データのキー * @return データ */ public static Object getData(String key) { DataObject data = dataMap.get(key); if (data == null) { return null; } data.lastAccessed = null; return data.data; } /** * (システム用です。スクリプトから呼び出す必要はありません。) * 変更があった場合は現在のデータをファイルに保存 * @throws IOException */ public static void store() throws IOException { if (!modified) { return; } Date time = new Date(); try (ZipOutputStream zos = new ZipOutputStream( new BufferedOutputStream(new FileOutputStream(AppConstants.SCRIPT_DATA_FILE)))) { for (Map.Entry<String, DataObject> entry : dataMap.entrySet()) { DataObject data = entry.getValue(); if (data.persist == false) { continue; } if (data.lastAccessed == null) { data.lastAccessed = time; } try { ZipEntry zipentry = new ZipEntry(entry.getKey()); zos.putNextEntry(zipentry); ObjectOutputStream oos = new ObjectOutputStream(zos); oos.writeObject(data); oos.flush(); } catch (IOException e) { LOG.get().warn("データの保存に失敗(" + entry.getKey() + ")", e); } } modified = false; } } /** * (システム用です。スクリプトから呼び出す必要はありません。) * ファイルから復元 * @throws IOException */ public static void load() throws IOException { if (AppConstants.SCRIPT_DATA_FILE.exists()) { try (ZipInputStream zis = new ZipInputStream( new BufferedInputStream(new FileInputStream(AppConstants.SCRIPT_DATA_FILE)))) { for (ZipEntry entry = zis.getNextEntry(); entry != null; entry = zis.getNextEntry()) { String key = entry.getName(); try { DataObject data = (DataObject) (new ObjectInputStream(zis).readObject()); data.persist = true; dataMap.put(key, data); } catch (ClassNotFoundException | ClassCastException e) { LOG.get().warn("データの読み込みに失敗(" + key + ")", e); } catch (IOException e) { LOG.get().warn("データの読み込みに失敗(" + key + ")", e); } } } } } /** * 記憶しているデータ数 * @return */ public static int size() { return dataMap.size(); } /** * (システム用です。スクリプトから呼び出す必要はありません。) * daysBefore日前以前のデータを削除 * @param daysBefore */ public static void cleanup(int daysBefore) { Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); calendar.add(Calendar.DATE, -daysBefore); Date time = calendar.getTime(); for (Map.Entry<String, DataObject> entry : dataMap.entrySet()) { Date lastAccessed = entry.getValue().lastAccessed; if ((lastAccessed != null) && lastAccessed.before(time)) { // 終了時までアクセスされなかったら保存されない entry.getValue().persist = false; } } } }