com.l2jfree.L2Config.java Source code

Java tutorial

Introduction

Here is the source code for com.l2jfree.L2Config.java

Source

/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.l2jfree;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.StringWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

import javolution.util.FastMap;
import javolution.util.FastSet;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import com.l2jfree.config.L2Properties;
import com.l2jfree.config.model.ConfigClassInfo;
import com.l2jfree.io.BufferedRedirectingOutputStream;
import com.l2jfree.lang.L2Math;
import com.l2jfree.lang.L2System;
import com.l2jfree.lang.L2TextBuilder;
import com.l2jfree.lang.management.DeadlockDetector;
import com.l2jfree.sql.DataSourceInitializer;
import com.l2jfree.sql.L2Database;
import com.l2jfree.sql.L2DatabaseInstaller;
import com.l2jfree.util.HandlerRegistry;
import com.l2jfree.util.concurrent.L2ThreadPool;
import com.l2jfree.util.concurrent.ThreadPoolInitializer;
import com.l2jfree.util.jar.ClassFinder;
import com.l2jfree.util.logging.L2Logger;

// LOW fill /doc folder
/**
 */
public abstract class L2Config {
    /**
     * Returns application lifetime in an user-friendly string.
     * 
     * @return time since launch
     */
    public static String getUptime() {
        final long uptimeInSec = (long) Math.ceil(ManagementFactory.getRuntimeMXBean().getUptime() / 1000.0);

        final long s = uptimeInSec / 1 % 60;
        final long m = uptimeInSec / 60 % 60;
        final long h = uptimeInSec / 3600 % 24;
        final long d = uptimeInSec / 86400;

        final L2TextBuilder tb = new L2TextBuilder();

        if (d > 0)
            tb.append(d + " day(s), ");

        if (h > 0 || tb.length() != 0)
            tb.append(h + " hour(s), ");

        if (m > 0 || tb.length() != 0)
            tb.append(m + " minute(s), ");

        if (s > 0 || tb.length() != 0)
            tb.append(s + " second(s)");

        return tb.moveToString();
    }

    /**
     * Returns application lifetime in a short string.
     * 
     * @return time since launch
     */
    public static String getShortUptime() {
        final long uptimeInSec = (long) Math.ceil(ManagementFactory.getRuntimeMXBean().getUptime() / 1000.0);

        final long s = uptimeInSec / 1 % 60;
        final long m = uptimeInSec / 60 % 60;
        final long h = uptimeInSec / 3600 % 24;
        final long d = uptimeInSec / 86400;

        final L2TextBuilder tb = new L2TextBuilder();

        if (d > 0)
            tb.append(d + "d");

        if (h > 0 || tb.length() != 0)
            tb.append(h + "h");

        if (m > 0 || tb.length() != 0)
            tb.append(m + "m");

        if (s > 0 || tb.length() != 0)
            tb.append(s + "s");

        return tb.moveToString();
    }

    /** Logging configuration file */
    public static final String LOG_FILE = "./config/logging.properties";

    /**
     * Defines the type of log entries that should be followed by a complete stack trace, regardless
     * if an exception is attached.
     */
    public static Level EXTENDED_LOG_LEVEL = Level.OFF;

    protected static final L2Logger _log;

    /** A stream where normal log messages are printed. */
    public static final PrintStream out = System.out;
    /** A stream where error messages are printed. */
    public static final PrintStream err = System.err;

    // TODO temporarily fixed, proper detection required
    public static boolean LAUNCHED_FROM_IDE = true;

    static {
        Locale.setDefault(Locale.ENGLISH);

        Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.err.print("Exception in thread \"" + t.getName() + "\" ");

                e.printStackTrace(System.err);

                // restart automatically
                if (e instanceof Error && !(e instanceof StackOverflowError))
                    Shutdown.halt(TerminationStatus.RUNTIME_UNCAUGHT_ERROR);
            }
        });

        for (Thread t : Thread.getAllStackTraces().keySet())
            t.setUncaughtExceptionHandler(Thread.getDefaultUncaughtExceptionHandler());

        if (System.getProperty("user.name").equals("root") && System.getProperty("user.home").equals("/root")) {
            System.out.print("L2jFree servers should not run under root-account ... exited.");
            Shutdown.exit(TerminationStatus.ENVIRONMENT_SUPERUSER);
        }

        final Map<String, List<String>> libs = new FastMap<String, List<String>>();

        final Set<File> files = new FastSet<File>();

        for (String classPath : System.getProperty("java.class.path").split(File.pathSeparator)) {
            final File classPathFile = new File(classPath);

            if (classPathFile.isDirectory()) {
                for (File f : classPathFile.listFiles())
                    files.add(f);
            } else
                files.add(classPathFile);
        }

        boolean shouldExit = false;

        for (File f : files) {
            if (!f.getName().endsWith("jar"))
                continue;

            final L2TextBuilder sb = new L2TextBuilder();

            final StringTokenizer st = new StringTokenizer(f.getName(), "-");

            tokenizer: while (st.hasMoreTokens()) {
                final String token = st.nextToken();

                boolean numberOnly = true;

                for (int i = 0; i < token.length(); i++) {
                    char c = token.charAt(i);

                    if (numberOnly && c == '.')
                        break tokenizer;

                    numberOnly &= Character.isDigit(c);
                }

                if (sb.length() != 0)
                    sb.append("-");

                sb.append(token);
            }

            String str = sb.moveToString();
            List<String> list = libs.get(str);

            if (list == null)
                libs.put(str, list = new ArrayList<String>());
            else
                shouldExit = true;

            list.add(f.getName());
        }

        if (shouldExit) {
            System.out.println("Server should not run with classpath conflicts! "
                    + "(rename/remove possible conflicting classpath entries)");

            for (Map.Entry<String, List<String>> entry : libs.entrySet())
                if (entry.getValue().size() > 1)
                    for (String name : entry.getValue())
                        System.out.println("\t'" + name + "'");

            Shutdown.exit(TerminationStatus.ENVIRONMENT_CP_CONFLICT);
        }

        System.setProperty("line.separator", "\r\n");
        System.setProperty("file.encoding", "UTF-8");
        System.setProperty("java.util.logging.manager", "com.l2jfree.util.logging.L2LogManager");

        try {
            // empty logging parameters
            final Properties props = new Properties();

            InputStream is = null;
            BufferedInputStream bis = null;
            try {
                // load default config from resources
                is = ClassLoader.getSystemResourceAsStream("logging.properties");

                props.load(is);

                // load optional project specific config (overwrites identical parameters)
                if (new File(LOG_FILE).exists()) {
                    bis = new BufferedInputStream(new FileInputStream(LOG_FILE));

                    props.load(bis);
                }
            } catch (Exception e) // IOException | RuntimeException
            {
                e.printStackTrace();
            } finally {
                IOUtils.closeQuietly(is);
                IOUtils.closeQuietly(bis);
            }

            if (props.isEmpty()) {
                LogManager.getLogManager().readConfiguration();
            } else {
                final StringWriter sw = new StringWriter();

                try {
                    props.store(sw, null);
                } catch (Exception e) // IOException | RuntimeException
                {
                    e.printStackTrace();
                }

                LogManager.getLogManager()
                        .readConfiguration(new ByteArrayInputStream(sw.toString().getBytes("UTF-8")));
            }
        } catch (Exception e) // IOException | RuntimeException
        {
            throw new Error(e);
        }

        _log = L2Logger.getLogger(L2Config.class);
        _log.info("logging initialized");

        System.setOut(new PrintStream(new BufferedRedirectingOutputStream() {
            @Override
            protected void handleLine(String line) {
                _log.logp(Level.INFO, getCaller(), line);
            }
        }));

        System.setErr(new PrintStream(new BufferedRedirectingOutputStream() {
            @Override
            protected void handleLine(String line) {
                _log.logp(Level.WARNING, getCaller(), line);
            }
        }));
    }

    private static StackTraceElement getCaller() {
        StackTraceElement[] stack = new Throwable().getStackTrace();

        for (int i = stack.length - 1; i >= 0; i--) {
            if (stack[i].getClassName().startsWith("java.io.")
                    || stack[i].getMethodName().equals("printStackTrace"))
                return stack[L2Math.limit(0, i + 1, stack.length - 1)];

            //if (stack[i].getMethodName().equals("dispatchUncaughtException"))
            //   break;
        }

        return null;
    }

    /** Flushes all pending log entries. */
    // FIXME MMOLogger.flush()
    public static void flush() {
        // those are redirected to loggers, so flush them first
        System.out.flush();
        System.err.flush();

        // then flush the loggers themselves
        final LogManager logManager = LogManager.getLogManager();

        for (String loggerName : Collections.list(logManager.getLoggerNames())) {
            if (loggerName == null)
                continue;

            final Logger logger = logManager.getLogger(loggerName);

            if (logger == null)
                continue;

            for (Handler handler : logger.getHandlers())
                if (handler != null)
                    handler.flush();
        }

        // and finally the real console streams
        L2Config.out.flush();
        L2Config.err.flush();
    }

    // TODO make sure to fit new config scheme, and move to config related classes
    private static final HandlerRegistry<String, ConfigLoader> _loaders = new HandlerRegistry<String, ConfigLoader>() {
        @Override
        public String standardizeKey(String key) {
            return key.trim().toLowerCase();
        }
    };

    protected static void registerConfig(ConfigLoader loader) {
        _loaders.register(loader.getName(), loader);
    }

    protected static void registerConfigClasses(String configPackageName) throws Exception {
        for (Class<?> clazz : ClassFinder.findClasses(configPackageName)) {
            if (ConfigPropertiesLoader.class.isAssignableFrom(clazz)) {
                ConfigPropertiesLoader config = (ConfigPropertiesLoader) clazz.newInstance();

                L2Config.registerConfig(config);
            }
        }
    }

    /**
     * Load all available configuration files.
     * 
     * @throws Exception if any config failed to load
     */
    public static void loadConfigs() throws Exception {
        for (ConfigLoader loader : _loaders.getHandlers(true).values())
            loader.load();
    }

    /**
     * Save all available configuration files.
     */
    public static void storeConfigs() {
        for (ConfigLoader loader : _loaders.getHandlers(true).values()) {
            final ConfigClassInfo info = loader.getConfigClassInfo();

            if (info != null)
                info.store();
        }
    }

    /**
     * Load the specified configuration file.
     * 
     * @param name Configuration name
     * @return the outcome of this call in a string
     * @throws Exception if the specified config could not be loaded
     */
    public static String loadConfig(String name) throws Exception {
        final ConfigLoader loader = _loaders.get(name);

        if (loader == null)
            throw new Exception();

        try {
            loader.load();
            return "'" + loader.getName() + "' reloaded!";
        } catch (Exception e) {
            return e.getMessage();
        }
    }

    /**
     * @return all available configuration names
     */
    public static String getLoaderNames() {
        return StringUtils.join(_loaders.getHandlers(true).keySet().iterator(), "|");
    }

    protected static abstract class ConfigLoader {
        protected abstract String getName();

        @SuppressWarnings("static-method")
        protected ConfigClassInfo getConfigClassInfo() {
            return null;
        }

        protected abstract void load() throws Exception;

        @Override
        public final int hashCode() {
            return getClass().hashCode();
        }

        @Override
        public final boolean equals(Object obj) {
            return getClass().equals(obj.getClass());
        }
    }

    protected static abstract class ConfigFileLoader extends ConfigLoader {
        protected abstract File getFile();

        @Override
        protected final void load() throws Exception {
            _log.info("loading '" + getFile() + "'");

            BufferedReader br = null;
            try {
                br = new BufferedReader(new FileReader(getFile()));

                loadReader(br);
            } catch (Exception e) {
                _log.fatal("Failed to load '" + getFile() + "'!", e);

                throw new Exception("Failed to load '" + getFile() + "'!");
            } finally {
                IOUtils.closeQuietly(br);
            }
        }

        protected abstract void loadReader(BufferedReader reader) throws Exception;
    }

    /** This class defines how to load configuration properties from a file. */
    public static abstract class ConfigPropertiesLoader extends ConfigFileLoader {
        protected ConfigPropertiesLoader() {
            getConfigClassInfo();
        }

        @Override
        protected final String getName() {
            return getConfigClassInfo().getConfigClass().fileName();
        }

        @Override
        protected final File getFile() {
            return getConfigClassInfo().getConfigFile();
        }

        @Override
        protected final ConfigClassInfo getConfigClassInfo() {
            try {
                return ConfigClassInfo.valueOf(getClass());
            } catch (Exception e) {
                throw new Error(e);
            }
        }

        @Override
        protected final void loadReader(BufferedReader reader) throws Exception {
            final L2Properties properties = new L2Properties(reader);

            getConfigClassInfo().load(properties);

            loadImpl(properties);

            getConfigClassInfo().store();
        }

        /**
         * @param properties
         */
        protected void loadImpl(L2Properties properties) {
            // load without annotations
        }
    }

    protected static void initApplication(Package configPackage,
            Class<? extends ThreadPoolInitializer> threadPoolInitializerClass) {
        initApplication(configPackage.getName(), threadPoolInitializerClass, null);
    }

    protected static void initApplication(String configPackageName,
            Class<? extends ThreadPoolInitializer> threadPoolInitializerClass) {
        initApplication(configPackageName, threadPoolInitializerClass, null);
    }

    protected static void initApplication(Package configPackage,
            Class<? extends ThreadPoolInitializer> threadPoolInitializerClass,
            Class<? extends DataSourceInitializer> dataSourceInitializerClass) {
        initApplication(configPackage.getName(), threadPoolInitializerClass, dataSourceInitializerClass);
    }

    protected static void initApplication(String configPackageName,
            Class<? extends ThreadPoolInitializer> threadPoolInitializerClass,
            Class<? extends DataSourceInitializer> dataSourceInitializerClass) {
        Util.printSection("Config");

        try {
            L2Config.registerConfigClasses(configPackageName);
        } catch (Exception e) {
            _log.fatal("Could not load configuration classes!", e);
            Shutdown.exit(TerminationStatus.RUNTIME_INVALID_CONFIGURATION);
        }

        try {
            L2Config.loadConfigs();
        } catch (Exception e) {
            _log.fatal("Could not load configuration files!", e);
            Shutdown.exit(TerminationStatus.RUNTIME_INVALID_CONFIGURATION);
        }

        Util.printSection("Thread Pool");

        try {
            L2ThreadPool.initThreadPools(threadPoolInitializerClass.newInstance());
        } catch (Exception e) {
            _log.fatal("Could not initialize thread pools!", e);
            Shutdown.exit(TerminationStatus.RUNTIME_INITIALIZATION_FAILURE);
        }

        if (dataSourceInitializerClass != null) {
            Util.printSection("Database");

            try {
                L2Database.setDataSource("default", dataSourceInitializerClass.newInstance());
            } catch (Exception e) {
                _log.fatal("Could not initialize DB connections!", e);
                Shutdown.exit(TerminationStatus.RUNTIME_INITIALIZATION_FAILURE);
            }

            Util.printSection("Database Installer");

            try {
                L2DatabaseInstaller.check();
            } catch (Exception e) {
                _log.fatal("Could not initialize DB tables!", e);
                Shutdown.exit(TerminationStatus.RUNTIME_INITIALIZATION_FAILURE);
            }
        }

        Util.printSection("Utility");

        Shutdown.initShutdownHook();

        DeadlockDetector.getInstance();
    }

    /**
     * To load full static utility classes.
     * 
     * @param clazz
     */
    protected static void load(Class<?> clazz) {
        try {
            Class.forName(clazz.getName());
        } catch (ClassNotFoundException e) {
            // should never happen
            throw new Error(e);
        }
    }

    protected static void applicationLoaded(String appName, String[] versionInfo, boolean dumpHeap) {
        Util.printSection("Summary");
        for (String line : versionInfo)
            _log.info(line);
        _log.info("Operating System: " + Util.getOSName() + " " + Util.getOSVersion() + " " + Util.getOSArch());
        _log.info("Available CPUs: " + Util.getAvailableProcessors());

        System.gc();
        System.runFinalization();

        for (String line : Util.getMemUsage())
            _log.info(line);

        _log.info("Server loaded in " + Util.formatNumber(ManagementFactory.getRuntimeMXBean().getUptime())
                + " milliseconds.");

        if (dumpHeap && !L2Config.LAUNCHED_FROM_IDE)
            L2System.dumpHeap(true);

        Util.printSection(appName);

        Startup.onStartup();
    }

    protected static void shutdownApplication() {
        try {
            L2Database.shutdown();
        } catch (Throwable t) {
            t.printStackTrace();
        }

        try {
            L2ThreadPool.shutdown();
        } catch (Throwable t) {
            t.printStackTrace();
        }

        try {
            L2Config.storeConfigs();
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
}