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.boot;
019
020import io.stallion.exceptions.CommandException;
021import io.stallion.fileSystem.BaseWatchEventHandler;
022import io.stallion.fileSystem.FileSystemWatcherRunner;
023import io.stallion.plugins.StallionJavaPlugin;
024import io.stallion.services.Log;
025import org.apache.commons.lang3.ArrayUtils;
026import org.apache.commons.lang3.exception.ExceptionUtils;
027
028
029import java.io.Console;
030import java.io.File;
031import java.util.ArrayList;
032import java.util.List;
033import java.util.logging.Level;
034
035/**
036 * Contains main method that gets run when stallion is executed. The entrypoint into the Stallion service.
037 */
038public class MainRunner
039
040{
041    /**
042     * Run Stallion
043     * @param args  - passed in via the command line
044     * @throws Exception
045     */
046    public static void main( String[] args ) throws Exception {
047        mainWithPlugins(args);
048    }
049
050    /**
051     * Run main, and manually register plugins, rather than have plugins loaded via the /jars folder.
052     *
053     * This method is used in plugin development, when we do not have a pre-built jar of the plugin available.
054     *
055     * @param args
056     * @param plugins
057     * @throws Exception
058     */
059    public static void mainWithPlugins(String[] args, StallionJavaPlugin...plugins) throws Exception {
060        try {
061            if (ArrayUtils.contains(args, "-autoReload")) {
062                runWithAutoReload(args, plugins);
063            } else {
064                doMain(args, plugins);
065            }
066        } catch (CommandException ex) {
067            System.err.println("\n\nError! \n\n" + ex.getMessage() + "\n\n");
068            if (Log.getLogLevel().intValue() <= Level.FINE.intValue()) {
069                Log.exception(ex, "Command exception");
070            }
071        }
072    }
073
074    /**
075     * Actually boot and run Stallion
076     *
077     * @param args
078     * @param plugins
079     * @throws Exception
080     */
081    public static void doMain(String[] args, StallionJavaPlugin[] plugins) throws Exception {
082        new Booter().boot(args, plugins);
083    }
084
085
086
087
088    private static boolean isDebugRunner = false;
089    private static boolean doReload = false;
090    private static FileSystemWatcherRunner watcher = null;
091    private static Thread watcherThread = null;
092    private static StallionServer server = null;
093
094    /**
095     * Runs the main Stallion service in auto-reload mode, which means that if a configuration file,
096     * or a server-side javascript file, or a plugin file is touched, the entire application context
097     * will reload and all server-side javascript will be re-processed. Use this when developing
098     * to avoid having to manually restart every time you change a file.
099     *
100     *
101     * @param args
102     * @param plugins
103     * @throws Exception
104     */
105    public static void runWithAutoReload( String[] args, StallionJavaPlugin[] plugins ) throws Exception {
106        isDebugRunner = true;
107
108        Console console = System.console();
109        while (true) {
110            Log.info("(re)start in debug reloading mode.");
111            try {
112                reboot(args, plugins);
113                if (doReload) {
114                    doReload = false;
115                    continue;
116                }
117                System.out.println("Interrupted. Type q to quit, any other key to reboot.");
118                String input = console.readLine();
119                if (input.startsWith("q")) {
120                    break;
121                }
122            } catch (Exception e) {
123                ExceptionUtils.printRootCauseStackTrace(e);
124                System.out.println("Other exception. Type q to quit, any other key to reboot.");
125                String input = console.readLine();
126                if (input.startsWith("q")) {
127                    break;
128                }
129            }
130        }
131        Log.info("Shutting down javascript and conf file watcher.");
132        if (watcher != null) {
133            watcher.setShouldRun(false);
134        }
135        watcherThread.interrupt();
136        watcherThread.join();
137        System.out.println("Exiting.");
138        System.exit(0);
139    }
140
141    static void setupWatchersIfDebugMode(String targetPath) {
142        if (isIsDebugRunner()) {
143            setupWatchers(targetPath);
144        } else {
145            return;
146        }
147    }
148
149    static void setupWatchers(String targetPath) {
150        List<File> directories = new ArrayList<>();
151        directories.add(new File(targetPath + "/js"));
152        directories.add(new File(targetPath + "/conf"));
153        if (new File(targetPath + "/plugins").isDirectory()) {
154            for(File file: new File(targetPath + "/plugins").listFiles()) {
155                if (file.isDirectory()) {
156                    directories.add(file);
157                }
158            }
159        }
160        watcher = new FileSystemWatcherRunner();
161
162
163        for (File dir: directories) {
164            Log.info("Watch folder {0}", dir.toString());
165            if (!dir.isDirectory()) {
166                continue;
167            }
168            BaseWatchEventHandler handler = new DebugFileChangeHandler()
169                    .setWatchTree(true)
170                    .setWatchedFolder(dir.getAbsolutePath());
171            if (dir.getAbsolutePath().endsWith("/conf")) {
172                handler.setExtension(".toml");
173            } else {
174                handler.setExtension(".js");
175            }
176            watcher.registerWatcher(handler);
177        }
178
179        watcherThread = new Thread(watcher);
180        watcherThread.setName("stallion-dev-mode-source-code-watcher");
181        watcherThread.start();
182
183    }
184
185
186    /**
187     * Reboot the application context.
188     *
189     * @param args
190     * @param plugins
191     * @throws Exception
192     */
193    public static void reboot(String[] args, StallionJavaPlugin[] plugins) throws Exception {
194        AppContextLoader.shutdown();
195        doMain(args, plugins);
196    }
197
198    static boolean isDoReload() {
199        return doReload;
200    }
201    static void setDoReload(boolean doReload) {
202        MainRunner.doReload = doReload;
203    }
204
205    static boolean isIsDebugRunner() {
206        return isDebugRunner;
207    }
208
209    static void setIsDebugRunner(boolean isDebugRunner) {
210        MainRunner.isDebugRunner = isDebugRunner;
211    }
212
213}