package org.openslx.dozmod; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import org.apache.log4j.Logger; import org.openslx.util.QuickTimer; import org.openslx.util.QuickTimer.Task; import org.openslx.util.Util; /** * Represents the configuration of the client * * @author Jonathan Bauer */ public class Config { /** * Logger for this class */ private final static Logger LOGGER = Logger.getLogger(Config.class); public static final int TRANSFER_TIMEOUT = 20 * 1000; // 20s timeout for hung transfers public static interface ErrorCallback { void writeError(Throwable t); } /** * Out property holder with all the setting keys */ private static final Properties prop = new Properties(); private static ErrorCallback errorCb = null; private static File configFile = null; private static boolean writePending = false; /** * Initializes the class by determining the path * to the config.ini on the system and setting the * private creating the ini member from that file. * * This function will make a distinction between * Linux and Windows OS's, as the standard paths * for configuration files obviously differ. * * @throws IOException */ public static void init() throws IOException { // Variables only needed locally String configPath = null; // Determine OS String osName = System.getProperty("os.name").toLowerCase(); LOGGER.info("Machine's OS: " + osName); if (osName.contains("windows")) { // Windows machine. Use the environment variable 'APPDATA' which // should point to a path similar to: // C:\Users\\AppData\Roaming String appDataPath = System.getenv("APPDATA"); if (!appDataPath.isEmpty()) { configPath = appDataPath; } else { // APPDATA was empty, let's guess LOGGER.warn("APPDATA is empty."); configPath = System.getProperty("user.home") + "\\AppData\\Roaming"; } } else if (osName.contains("linux")) { configPath = System.getProperty("user.home") + "/.config"; } if (configPath == null || configPath.isEmpty()) { // Not Windows nor Linux, try fallback to relative path // TODO MacOS Support? configPath = "."; } // Check if we got a path configFile = new File(configPath + File.separatorChar + "bwSuite" + File.separatorChar + "config.properties"); // Check if the directory exists. if (!configFile.getParentFile().exists()) { LOGGER.info("Folder " + configFile.getParentFile() + " does not exist, creating it."); // Does not, create it if (!configFile.getParentFile().mkdirs()) { throw new IOException("Could not create '" + configFile.getParentFile() + "'."); } } if (!configFile.exists()) { forceSaveInternal(); } // Make sure all settings are saved when we exit Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { Config.forceSave(); } }); // Load configuration from java properties file InputStream in = new FileInputStream(configFile); try { prop.load(in); } finally { in.close(); } } private static void forceSaveInternal() throws FileNotFoundException, IOException { synchronized (prop) { prop.store(new FileOutputStream(configFile), "bwLehrpool Dozentenmodul Client Config"); } } public static boolean forceSave() { synchronized (prop) { if (!writePending) return true; writePending = false; if (configFile == null) return false; try { forceSaveInternal(); return true; } catch (Exception e) { if (errorCb != null) errorCb.writeError(e); } return false; } } /** * Called internally by all set[TYPE]() methods. This triggers * a save-task in one second, if one isn't pending yet. This is so * we don't write the file many times, possibly concurrently, if lots * of settings are changed. */ private static void queueSave() { synchronized (prop) { if (writePending) return; writePending = true; QuickTimer.scheduleOnce(new Task() { @Override public void fire() { forceSave(); } }, 1000); } } public static void setErrorCallback(ErrorCallback cb) { errorCb = cb; } /** * Query the path of the configuration file * * @return path to the configuration file */ public static String getPath() { return configFile.getParent(); } /* * Getters and setters for the various config options */ /** * Query the value of 'BillOfRights' from the configuration file. * * @return true if the user already accepted bill of rights, false * otherwise. */ public static int getDisclaimerAgreement() { return getInteger("disclaimer.accepted_version", 0); } /** * Sets the value of 'BillOfRights' in the configuration file to 'value' * * @return true if it succeeded, false otherwise */ public static void setDisclaimerAgreement(int value) { setInteger("disclaimer.accepted_version", value); } public static boolean getVirtualizerRead() { return getBoolean("notice.virtualizer", false); } public static void setVirtualizerRead(boolean selection) { setBoolean("notice.virtualizer", selection); } /** * Get the remembered user, if set. * * @return user name if saved, an empty string otherwise. */ public static String getUsername() { return getString("login.name", ""); } /** * Sets the name of the remembered user * * @return true if it succeeded, false otherwise */ public static void setUsername(String value) { setString("login.name", value); } /** * Query the value of 'Letzter Downloadpfad' from the configuration file. * * @return last download path if saved, the path to the user's home * otherwise. */ public static String getDownloadPath() { return getString("download.path", System.getProperty("user.home")); } /** * Sets the value of 'Letzter Downloadpfad' in the configuration file to * 'value' * * @return true if it succeeded, false otherwise */ public static void setDownloadPath(String value) { setString("download.path", value); } /** * Query the value of 'Letzter Uploadpfad' from the configuration file. * * @return last upload path if saved, the path to the user's home otherwise. */ public static String getUploadPath() { return getString("upload.path", System.getProperty("user.home")); } /** * Sets the value of "Letzter Uploadpfad" in the configuration file to * 'value' * * @return true if it succeeded, false otherwise */ public static void setUploadPath(String value) { setString("upload.path", value); } /** * Query the IdP of the configuration file * * @return stored IdP */ public static String getIdentityProvider() { return getString("login.idp", ""); } /** * Sets the value of "IdP" in the configuration file to 'value' * * @return true if it succeeded, false otherwise */ public static void setIdentityProvider(String value) { setString("login.idp", value); } /** * Query the authentication method of the configuration file * * @return stored IdP */ public static String getAuthenticationMethod() { return getString("login.method", "ECP"); } /** * Sets the value of the selected authentication method in the configuration * file to 'value' * * @return true if it succeeded, false otherwise */ public static void setAuthenticationMethod(String value) { setString("login.method", value); } /** * Saves the current session, identified by the satellite server's address * and token. * * @param satAddress * @param satToken */ public static void saveCurrentSession(String satAddress, String satToken, String masterToken) { setString("session.address", satAddress); setString("session.token", satToken); setString("session.mastertoken", masterToken); } /** * Load a saved session. * * @return Saved session, or null if no session was saved */ public static SavedSession getSavedSession() { SavedSession session = new SavedSession(getString("session.address", ""), getString("session.token", ""), getString("session.mastertoken", "")); if (session.token.isEmpty() || session.address.isEmpty() || session.masterToken.isEmpty()) return null; return session; } /* * Generic helpers for different data types */ /** * Gets the boolean from the given key. * If nothing is found, return the given default value * * @param key key to query in that section * @param defaultValue default value to be returned, if none is found. * @return */ private static boolean getBoolean(String key, boolean defaultValue) { return Boolean.parseBoolean(prop.getProperty(key, Boolean.toString(defaultValue))); } /** * Sets the given key to value. * * @param key key to set * @param value value to assign to key */ private static void setBoolean(String key, boolean value) { prop.setProperty(key, Boolean.toString(value)); queueSave(); } /** * Gets the integer from the given key. * If nothing is found, return the given default value * * @param key key to query in that section * @param defaultValue default value to be returned, if none is found. * @return */ private static int getInteger(String key, int defaultValue) { return Util.parseInt(prop.getProperty(key), defaultValue); } /** * Sets the given key to value. * * @param key key to set * @param value value to assign to key */ private static void setInteger(String key, int value) { prop.setProperty(key, Integer.toString(value)); queueSave(); } /** * Gets the string from the given key. * If nothing is found, return the given default value * * @param key key to lookup in the section * @param defaultValue default value to return if none is found in the file * @return */ private static String getString(String key, String defaultValue) { return prop.getProperty(key, defaultValue); } /** * Sets the given 'key' in the given 'section' to 'value'. * Restricted to string. * * @param key key to set * @param value value to assign to key */ private static void setString(String key, String value) { prop.setProperty(key, value); queueSave(); } public static class SavedSession { public final String address; public final String token; public final String masterToken; public SavedSession(String address, String token, String masterToken) { this.address = address; this.token = token; this.masterToken = masterToken; } } }