org.gldapdaemon.core.Configurator.java Source code

Java tutorial

Introduction

Here is the source code for org.gldapdaemon.core.Configurator.java

Source

//
// GCALDaemon is an OS-independent Java program that offers two-way
// synchronization between Google Calendar and various iCalalendar (RFC 2445)
// compatible calendar applications (Sunbird, Rainlendar, iCal, Lightning, etc).
//
// Apache License
// Version 2.0, January 2004
// http://www.apache.org/licenses/
// 
// Project home:
// http://gcaldaemon.sourceforge.net
//
package org.gldapdaemon.core;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Properties;
import java.util.TimeZone;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.SimpleLayout;
import org.gldapdaemon.core.ldap.ContactLoader;

/**
 * Config loader, property setter, and listener starter object.
 * 
 * Created: Jan 03, 2007 12:50:56 PM
 * 
 * @author Andras Berkes
 */
public final class Configurator {

    // --- COMMON CONSTANTS ---
    public static final String VERSION = "GCALDaemon V1.0 beta 16";
    public static final byte MODE_DAEMON = 0;
    public static final byte MODE_RUNONCE = 1;
    public static final byte MODE_CONFIGEDITOR = 2;
    public static final byte MODE_EMBEDDED = 3;
    private static final int MAX_CACHE_SIZE = 100;
    private static final SimpleDateFormat BACKUP_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
    // --- SIMPLE CONFIG CONSTANTS ---
    public static final String REMOTE_DELETE_ENABLED = "remote.delete.enabled";
    public static final String FILE_POLLING_FILE = "file.polling.file";
    public static final String FILE_RELOADER_SCRIPT = "file.reloader.script";
    public static final String LDAP_VCARD_ENCODING = "ldap.vcard.encoding";
    public static final String MAILTERM_DIR_PATH = "mailterm.dir.path";
    public static final String FEED_CACHE_TIMEOUT = "feed.cache.timeout";
    public static final String FILE_ENABLED = "file.enabled";
    public static final String NOTIFIER_LOCAL_USERS = "notifier.local.users";
    public static final String HTTP_ALLOWED_ADDRESSES = "http.allowed.addresses";
    public static final String PROXY_PASSWORD = "proxy.password";
    public static final String PROXY_USERNAME = "proxy.username";
    public static final String HTTP_ENABLED = "http.enabled";
    public static final String SENDMAIL_ENABLED = "sendmail.enabled";
    public static final String LDAP_GOOGLE_PASSWORD = "ldap.google.password";
    public static final String LDAP_GOOGLE_USERNAME = "ldap.google.username";
    public static final String NOTIFIER_WINDOW_SOUND = "notifier.window.sound";
    public static final String ICAL_BACKUP_TIMEOUT = "ical.backup.timeout";
    public static final String SEND_INVITATIONS = "send.invitations";
    public static final String MAILTERM_GOOGLE_PASSWORD = "mailterm.google.password";
    public static final String MAILTERM_GOOGLE_USERNAME = "mailterm.google.username";
    public static final String LDAP_CACHE_TIMEOUT = "ldap.cache.timeout";
    public static final String MAILTERM_ALLOWED_ADDRESSES = "mailterm.allowed.addresses";
    public static final String PROXY_PORT = "proxy.port";
    public static final String FEED_DUPLICATION_FILTER = "feed.duplication.filter";
    public static final String FEED_ENABLED = "feed.enabled";
    public static final String LDAP_ALLOWED_HOSTNAMES = "ldap.allowed.hostnames";
    public static final String SENDMAIL_GOOGLE_PASSWORD = "sendmail.google.password";
    public static final String SENDMAIL_GOOGLE_USERNAME = "sendmail.google.username";
    public static final String LDAP_ENABLED = "ldap.enabled";
    public static final String MAILTERM_CONSOLE_ENCODING = "mailterm.console.encoding";
    public static final String NOTIFIER_WINDOW_STYLE = "notifier.window.style";
    public static final String LDAP_VCARD_VERSION = "ldap.vcard.version";
    public static final String SENDMAIL_DIR_PATH = "sendmail.dir.path";
    public static final String LOG_CONFIG = "log.config";
    public static final String HTTP_PORT = "http.port";
    public static final String MAILTERM_POLLING_GOOGLE = "mailterm.polling.google";
    public static final String LDAP_ALLOWED_ADDRESSES = "ldap.allowed.addresses";
    public static final String PROGRESS_ENABLED = "progress.enabled";
    public static final String MAILTERM_MAIL_SUBJECT = "mailterm.mail.subject";
    public static final String SENDMAIL_POLLING_DIR = "sendmail.polling.dir";
    public static final String FEED_EVENT_LENGTH = "feed.event.length";
    public static final String PROXY_HOST = "proxy.host";
    public static final String NOTIFIER_DATE_FORMAT = "notifier.date.format";
    public static final String EXTENDED_SYNC_ENABLED = "extended.sync.enabled";
    public static final String HTTP_ALLOWED_HOSTNAMES = "http.allowed.hostnames";
    public static final String FILE_OFFLINE_ENABLED = "file.offline.enabled";
    public static final String MAILTERM_ENABLED = "mailterm.enabled";
    public static final String NOTIFIER_GOOGLE_PASSWORD = "notifier.google.password";
    public static final String NOTIFIER_POLLING_MAILBOX = "notifier.polling.mailbox";
    public static final String NOTIFIER_GOOGLE_USERNAME = "notifier.google.username";
    public static final String NOTIFIER_ENABLED = "notifier.enabled";
    public static final String CACHE_TIMEOUT = "cache.timeout";
    public static final String FILE_POLLING_GOOGLE = "file.polling.google";
    public static final String LDAP_PORT = "ldap.port";
    public static final String EDITOR_LANGUAGE = "editor.language";
    public static final String EDITOR_LOOK_AND_FEEL = "editor.look.and.feel";
    public static final String WORK_DIR = "work.dir";
    public static final String REMOTE_ALARM_TYPES = "remote.alarm.types";
    // --- FILE CONFIG CONSTANTS ---
    public static final String FILE_PRIVATE_ICAL_URL = "file.private.ical.url";
    public static final String FILE_ICAL_PATH = "file.ical.path";
    public static final String FILE_GOOGLE_USERNAME = "file.google.username";
    public static final String FILE_GOOGLE_PASSWORD = "file.google.password";
    // --- UTILS ---
    private Properties config = new Properties();
    private final File workDirectory;
    private final boolean standaloneMode;
    private final byte mode;
    private File configFile;
    // --- SERVICES AND LISTENERS ---
    private Thread gmailPool;
    private Thread contactLoader;
    // --- FEED CONVERTER'S PARAMETERS ---
    protected final double duplicationRatio;

    // --- CONSTRUCTOR ---
    public Configurator(String configPath, Properties properties, boolean userHome, byte mode) throws Exception {
        this.mode = mode;
        int i;
        File programRootDir = null;
        if (mode == MODE_EMBEDDED) {

            // Embedded mode
            standaloneMode = false;
            config = properties;
            String workPath = getConfigProperty(WORK_DIR, null);
            workDirectory = new File(workPath);
        } else {

            // Load config
            if (configPath != null) {
                configFile = new File(configPath);
            }
            InputStream in = null;
            boolean configInClassPath = false;
            if (configFile == null || !configFile.isFile()) {
                try {
                    in = Configurator.class.getResourceAsStream("/gcal-daemon.cfg");
                    configInClassPath = in != null;
                } catch (Exception ignored) {
                    in = null;
                }
                if (in == null) {
                    System.out.println("INFO  | Searching main configuration file...");
                    String path = (new File("x")).getAbsolutePath().replace('\\', '/');
                    i = path.lastIndexOf('/');
                    if (i > 1) {
                        i = path.lastIndexOf('/', i - 1);
                        if (i > 1) {
                            configFile = new File(path.substring(0, i), "conf/gcal-daemon.cfg");
                        }
                    }
                    if (configFile == null || !configFile.isFile()) {
                        configFile = new File("/usr/local/sbin/GCALDaemon/conf/gcal-daemon.cfg");
                    }
                    if (configFile == null || !configFile.isFile()) {
                        configFile = new File("/GCALDaemon/conf/gcal-daemon.cfg");
                    }
                    if (configFile == null || !configFile.isFile()) {
                        File root = new File("/");
                        String[] dirs = root.list();
                        if (dirs != null) {
                            for (i = 0; i < dirs.length; i++) {
                                configFile = new File('/' + dirs[i] + "/GCALDaemon/conf/gcal-daemon.cfg");
                                if (configFile.isFile()) {
                                    break;
                                }
                            }
                        }
                    }
                    if (configFile == null || !configFile.isFile()) {
                        throw new FileNotFoundException("Missing main configuration file: " + configPath);
                    }
                    if (!userHome) {

                        // Open global config file
                        in = new FileInputStream(configFile);
                    }
                }
            } else {
                if (!userHome) {
                    // Open global config file
                    in = new FileInputStream(configFile);
                }
            }
            standaloneMode = !configInClassPath;
            if (in != null) {

                // Load global config file
                config.load(new BufferedInputStream(in));
                in.close();
            }

            // Loading config from classpath
            if (configFile == null) {
                try {
                    URL url = Configurator.class.getResource("/gcal-daemon.cfg");
                    configFile = new File(url.getFile());
                } catch (Exception ignored) {
                }
            }
            programRootDir = configFile.getParentFile().getParentFile();
            System.setProperty("gldapdaemon.program.dir", programRootDir.getAbsolutePath());
            String workPath = getConfigProperty(WORK_DIR, null);
            File directory;
            if (workPath == null) {
                directory = new File(programRootDir, "work");
            } else {
                directory = new File(workPath);
            }
            if (!directory.isDirectory()) {
                if (!directory.mkdirs()) {
                    directory = new File("work");
                    directory.mkdirs();
                }
            }
            workDirectory = directory;

            // User-specific config file handler
            if (userHome) {
                boolean useGlobal = true;
                try {
                    String home = System.getProperty("user.home", null);
                    if (home != null) {
                        File userConfig = new File(home, ".gcaldaemon/gcal-daemon.cfg");
                        if (!userConfig.isFile()) {

                            // Create new user-specific config
                            File userDir = new File(home, ".gcaldaemon");
                            userDir.mkdirs();
                            copyFile(configFile, userConfig);
                            if (!userConfig.isFile()) {
                                userConfig.delete();
                                userDir.delete();
                            }
                        }
                        if (userConfig.isFile()) {

                            // Load user-specific config
                            configFile = userConfig;
                            in = new FileInputStream(configFile);
                            config.load(new BufferedInputStream(in));
                            in.close();
                            useGlobal = false;
                        }
                    }
                } catch (Exception ignored) {
                }
                if (useGlobal) {

                    // Load global config file
                    config.load(new BufferedInputStream(in));
                    in.close();
                }
            }
        }

        // Init logger
        ProgressMonitor monitor = null;
        if (standaloneMode && mode != MODE_CONFIGEDITOR) {

            // Compute log config path
            String logConfig = getConfigProperty(LOG_CONFIG, "logger-config.cfg");
            logConfig = logConfig.replace('\\', '/');
            File logConfigFile;
            if (logConfig.indexOf('/') == -1) {
                logConfigFile = new File(programRootDir, "conf/" + logConfig);
            } else {
                logConfigFile = new File(logConfig);
            }
            if (logConfigFile.isFile()) {
                String logConfigPath = logConfigFile.getAbsolutePath();
                System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.Log4JLogger");
                System.setProperty("log4j.defaultInitOverride", "false");
                System.setProperty("log4j.configuration", logConfigPath);
                try {
                    PropertyConfigurator.configure(logConfigPath);
                } catch (Throwable ignored) {
                    ignored.printStackTrace();
                }
            }
        }
        if (mode == MODE_CONFIGEDITOR) {

            // Show monitor
            try {
                monitor = new ProgressMonitor();
                monitor.setVisible(true);
                Thread.sleep(400);
            } catch (Exception ignored) {
            }

            // Init simple logger
            try {
                System.setProperty("log4j.defaultInitOverride", "false");
                Logger root = Logger.getRootLogger();
                root.removeAllAppenders();
                root.addAppender(new ConsoleAppender(new SimpleLayout()));
                root.setLevel(Level.INFO);
            } catch (Throwable ingored) {
            }
        }

        // Disable unnecessary INFO messages of the GData API
        try {
            java.util.logging.Logger logger = java.util.logging.Logger.getLogger("com.google");
            logger.setLevel(java.util.logging.Level.WARNING);
        } catch (Throwable ingored) {
        }

        Log log = LogFactory.getLog(Configurator.class);
        log.info(VERSION + " starting...");
        if (configFile != null && log.isDebugEnabled()) {
            log.debug("Config loaded successfully (" + configFile + ").");
        }

        // Check Java version
        double jvmVersion = 1.5;
        try {
            jvmVersion = Float.valueOf(System.getProperty("java.version", "1.5").substring(0, 3)).floatValue();
        } catch (Exception ignored) {
        }
        if (jvmVersion < 1.5) {
            log.fatal("GCALDaemon requires at least Java 1.5! Current version: "
                    + System.getProperty("java.version"));
            throw new Exception("Invalid JVM version!");
        }

        // Check permission
        if (workDirectory.isDirectory() && !workDirectory.canWrite()) {
            if (System.getProperty("os.name", "unknown").toLowerCase().indexOf("windows") == -1) {
                String path = workDirectory.getCanonicalPath();
                if (programRootDir != null) {
                    path = programRootDir.getCanonicalPath();
                }
                log.warn("Please check the file permissions on the '" + workDirectory.getCanonicalPath()
                        + "' folder!\r\n" + "Hint: [sudo] chmod -R 777 " + path);
            }
        }

        // Disable SSL validation
        try {
            // Create a trust manager that does not validate certificate chains
            javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[] {
                    new javax.net.ssl.X509TrustManager() {

                        public final java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }

                        public final void checkClientTrusted(java.security.cert.X509Certificate[] certs,
                                String authType) {
                        }

                        public final void checkServerTrusted(java.security.cert.X509Certificate[] certs,
                                String authType) {
                        }
                    } };

            // Install the all-trusting trust manager
            javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
            javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        } catch (Throwable ignored) {
        }

        // Replace hostname verifier
        try {
            javax.net.ssl.HostnameVerifier hv[] = new javax.net.ssl.HostnameVerifier[] {
                    new javax.net.ssl.HostnameVerifier() {

                        public final boolean verify(String hostName, javax.net.ssl.SSLSession session) {
                            return true;
                        }
                    } };

            javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(hv[0]);
        } catch (Throwable ignored) {
        }

        // Setup proxy
        String proxyHost = getConfigProperty(PROXY_HOST, null);
        if (proxyHost != null) {
            String proxyPort = getConfigProperty(PROXY_PORT, null);
            if (proxyPort == null) {
                log.warn("Missing 'proxy.port' configuration property!");
            } else {

                // HTTP proxy server properties
                System.setProperty("http.proxyHost", proxyHost);
                System.setProperty("http.proxyPort", proxyPort);
                System.setProperty("http.proxySet", "true");

                // HTTPS proxy server properties
                System.setProperty("https.proxyHost", proxyHost);
                System.setProperty("https.proxyPort", proxyPort);
                System.setProperty("https.proxySet", "true");

                // Setup proxy credentials
                String username = getConfigProperty(PROXY_USERNAME, null);
                String encodedPassword = getConfigProperty(PROXY_PASSWORD, null);
                if (username != null) {
                    if (encodedPassword == null) {
                        log.warn("Missing 'proxy.password' configuration property!");
                    } else {
                        String password = StringUtils.decodePassword(encodedPassword);

                        // HTTP auth credentials
                        System.setProperty("http.proxyUser", username);
                        System.setProperty("http.proxyUserName", username);
                        System.setProperty("http.proxyPassword", password);

                        // HTTPS auth credentials
                        System.setProperty("https.proxyUser", username);
                        System.setProperty("https.proxyUserName", username);
                        System.setProperty("https.proxyPassword", password);
                    }
                }
            }
        }

        // Get feed event duplication ratio
        String percent = getConfigProperty(FEED_DUPLICATION_FILTER, "70").trim();
        if (percent.endsWith("%")) {
            percent = percent.substring(0, percent.length() - 1).trim();
        }
        double ratio = Double.parseDouble(percent) / 100;
        if (ratio < 0.4) {
            ratio = 0.4;
            log.warn("The smallest enabled filter percent is '40%'!");
        } else {
            if (ratio > 1) {
                log.warn("The largest filter percent is '100%'!");
                ratio = 1;
            }
        }
        duplicationRatio = ratio;

        // Displays time zone
        log.info("Local time zone is " + TimeZone.getDefault().getDisplayName() + ".");

        // Get main thread group
        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
        while (mainGroup.getParent() != null) {
            mainGroup = mainGroup.getParent();
        }

        // Init Gmail pool
        boolean enableLDAP = getConfigProperty(LDAP_ENABLED, false);
        if (enableLDAP) {
            gmailPool = startService(log, mainGroup, "org.gldapdaemon.core.GmailPool");
        }

        // Init LDAP listener
        if (enableLDAP) {
            contactLoader = startService(log, mainGroup, "org.gldapdaemon.core.ldap.ContactLoader");
        } else {
            if (standaloneMode) {
                log.info("LDAP server disabled.");
            }
        }

        // Clear configuration holder
        config.clear();
    }

    private final Thread startService(Log log, ThreadGroup group, String name) throws Exception {
        try {
            Class serviceClass = Class.forName(name);
            Class[] types = new Class[2];
            types[0] = ThreadGroup.class;
            types[1] = Configurator.class;
            Constructor constructor = serviceClass.getConstructor(types);
            Object[] values = new Object[2];
            values[0] = group;
            values[1] = this;
            return (Thread) constructor.newInstance(values);
        } catch (Exception configError) {
            String message = configError.getMessage();
            Throwable cause = configError.getCause();
            while (cause != null) {
                if (cause.getMessage() != null) {
                    message = cause.getMessage();
                }
                cause = cause.getCause();
            }
            log.fatal(message.toUpperCase(), configError);
            throw configError;
        }
    }

    public final byte getRunMode() {
        return mode;
    }

    public final File getConfigFile() {
        return configFile;
    }

    public static final void copyFile(File from, File to) throws Exception {
        if (from == null || to == null || !from.exists()) {
            return;
        }
        RandomAccessFile fromFile = null;
        RandomAccessFile toFile = null;
        try {
            fromFile = new RandomAccessFile(from, "r");
            toFile = new RandomAccessFile(to, "rw");
            FileChannel fromChannel = fromFile.getChannel();
            FileChannel toChannel = toFile.getChannel();
            long length = fromFile.length();
            long start = 0;
            while (start < length) {
                start += fromChannel.transferTo(start, length - start, toChannel);
            }
            fromChannel.close();
            toChannel.close();
        } finally {
            if (fromFile != null) {
                fromFile.close();
            }
            if (toFile != null) {
                toFile.close();
            }
        }
    }

    // --- COMMON CONFIGURATION PROPERTY GETTERS ---
    public final String getConfigProperty(String name, String defaultValue) {
        String value = config.getProperty(name, defaultValue);
        if (value == null) {
            return defaultValue;
        } else {
            value = value.trim();
            if (value.length() == 0) {
                return defaultValue;
            }
        }
        return value;
    }

    public final boolean getConfigProperty(String name, boolean defaultValue) {
        String bool = config.getProperty(name, Boolean.toString(defaultValue)).toLowerCase();
        return "true".equals(bool) || "on".equals(bool) || "1".equals(bool);
    }

    public final long getConfigProperty(String name, long defaultValue) throws Exception {
        String number = config.getProperty(name, Long.toString(defaultValue));
        try {
            return StringUtils.stringToLong(number);
        } catch (Exception malformed) {
            throw new IllegalArgumentException("Malformed numeric parameter (" + name + ")!");
        }
    }

    public final FilterMask[] getFilterProperty(String name) throws Exception {
        return getFilterProperty(name, false);
    }

    public final FilterMask[] getFilterProperty(String name, boolean ignoreCase) throws Exception {
        String list = config.getProperty(name, null);
        try {
            return StringUtils.splitMaskList(list, ignoreCase);
        } catch (Exception malformed) {
            throw new IllegalArgumentException("Malformed mask list (" + name + ")!");
        }
    }

    public final String getPasswordProperty(String name) throws Exception {
        String encodedPassword = config.getProperty(name, null);
        if (encodedPassword == null) {
            throw new IllegalArgumentException("Missing password (" + name + ")!");
        }
        try {
            return StringUtils.decodePassword(encodedPassword);
        } catch (Exception malformed) {
            throw new IllegalArgumentException("Malformed password (" + name + ")!");
        }
    }

    public final File getWorkDirectory() {
        return workDirectory;
    }

    // --- GMAIL ADDRESS BOOK ---
    private volatile boolean started = false;

    public final ArrayList<GmailContact> getAddressBook() throws Exception {
        ArrayList<GmailContact> contacts = null;
        if (contactLoader != null) {
            ContactLoader loader = (ContactLoader) contactLoader;
            try {
                contacts = loader.getContacts();
                if (!started) {
                    started = true;
                    if (contacts == null) {
                        for (int i = 0; i < 5; i++) {
                            contacts = loader.getContacts();
                            if (contacts != null) {
                                break;
                            }
                            Thread.sleep(2000);
                        }
                    }
                }
            } catch (InterruptedException interrupt) {
                throw interrupt;
            } catch (Exception ignored) {
            }
        }
        return contacts;
    }

    // --- COMMON GMAIL POOL ---
    public final GmailPool getGmailPool() {
        return (GmailPool) gmailPool;
    }

    // --- STANDALONE APPLICATION MARKER ---
    public final boolean isStandalone() {
        return standaloneMode;
    }

    // --- STOP LISTENERS ---
    public final void interrupt() {

        // Stop services
        stopService(contactLoader);
        stopService(gmailPool);
    }

    private static final void stopService(Thread service) {
        if (service != null) {
            try {
                service.interrupt();
            } catch (Exception ignored) {
            }
        }
    }
}