io.janusproject.Boot.java Source code

Java tutorial

Introduction

Here is the source code for io.janusproject.Boot.java

Source

/*
 * $Id$
 *
 * Janus platform is an open-source multiagent platform.
 * More details on http://www.janusproject.io
 *
 * Copyright (C) 2014 Sebastian RODRIGUEZ, Nicolas GAUD, Stphane GALLAND.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.janusproject;

import io.janusproject.kernel.Kernel;
import io.janusproject.services.network.NetworkConfig;
import io.janusproject.util.LoggerCreator;
import io.sarl.lang.core.Agent;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.arakhne.afc.vmutil.locale.Locale;

import com.google.inject.Module;

/** This is the class that permits to boot the Janus platform.
 * <p>
 * This class provides the "main" function for the platform.
 * The list of the parameters is composed of a list of options,
 * the classname of an agent to launch, and the parameters to pass
 * to the launched agent.
 * <p>
 * The supported options may be obtain by passing no parameter, or
 * the option <code>-h</code>.
 * <p>
 * Example of Janus launching with Maven:
 * <pre><code>mvn exec:java
 *     -Dexec.mainClass="io.janusproject.Boot"
 *     -Dexec.args="my.Agent"</code></pre>
 * <p>
 * Example of Janus launching from the CLI (only with the Jar file that is containing
 * all the jar dependencies):
 * <pre><code>java -jar janus-with-dependencies.jar my.Agent</code></pre>
 *
 * @author $Author: srodriguez$
 * @author $Author: sgalland$
 * @version $FullVersion$
 * @mavengroupid $GroupId$
 * @mavenartifactid $ArtifactId$
 */
public final class Boot {
    private static final int ERROR_EXIT_CODE = 255;

    private Boot() {
        //
    }

    private static void parseCommandForInfoOptions(CommandLine cmd) {
        if (cmd.hasOption('h') || cmd.getArgs().length == 0) {
            showHelp();
        }
        if (cmd.hasOption('s')) {
            showDefaults();
        }
    }

    private static void parseCommandLineForSystemProperties(CommandLine cmd) {
        if (cmd.hasOption('o')) {
            System.setProperty(JanusConfig.OFFLINE, Boolean.TRUE.toString());
        }
        if (cmd.hasOption('R')) {
            System.setProperty(JanusConfig.BOOT_DEFAULT_CONTEXT_ID_NAME, Boolean.FALSE.toString());
            System.setProperty(JanusConfig.RANDOM_DEFAULT_CONTEXT_ID_NAME, Boolean.TRUE.toString());
        } else if (cmd.hasOption('B')) {
            System.setProperty(JanusConfig.BOOT_DEFAULT_CONTEXT_ID_NAME, Boolean.TRUE.toString());
            System.setProperty(JanusConfig.RANDOM_DEFAULT_CONTEXT_ID_NAME, Boolean.FALSE.toString());
        } else if (cmd.hasOption('W')) {
            System.setProperty(JanusConfig.BOOT_DEFAULT_CONTEXT_ID_NAME, Boolean.FALSE.toString());
            System.setProperty(JanusConfig.RANDOM_DEFAULT_CONTEXT_ID_NAME, Boolean.FALSE.toString());
        }
        // Define the system properties, if not already done by the JRE.
        Properties props = cmd.getOptionProperties("D"); //$NON-NLS-1$
        if (props != null) {
            for (Entry<Object, Object> entry : props.entrySet()) {
                System.setProperty(entry.getKey().toString(), entry.getValue().toString());
            }
        }
    }

    private static void parseCommandLineForVerbosity(CommandLine cmd) {
        // The order of the options is important.
        int verbose = LoggerCreator.toInt(JanusConfig.VERBOSE_LEVEL_VALUE);
        if (cmd.hasOption('v') || cmd.hasOption('q') || cmd.hasOption('l')) {
            @SuppressWarnings("unchecked")
            Iterator<Option> optIterator = cmd.iterator();
            while (optIterator.hasNext()) {
                Option opt = optIterator.next();
                switch (opt.getOpt()) {
                case "l": //$NON-NLS-1$
                    verbose = LoggerCreator.toInt(opt.getValue());
                    break;
                case "q": //$NON-NLS-1$
                    --verbose;
                    break;
                case "v": //$NON-NLS-1$
                    ++verbose;
                    break;
                default:
                }
            }
            System.setProperty(JanusConfig.VERBOSE_LEVEL_NAME, Integer.toString(verbose));
        }

        // Show the Janus logo?
        if (cmd.hasOption("nologo") || verbose == 0) { //$NON-NLS-1$
            System.setProperty(JanusConfig.JANUS_LOGO_SHOW_NAME, Boolean.FALSE.toString());
        }
    }

    /** Parse the command line.
     *
     * @param args - the CLI arguments given to the program.
     * @param propertyFiles - files that may be filled with the filenames given on the CLI.
     * @return the arguments that are not recognized as CLI options.
     */
    public static String[] parseCommandLine(String[] args, List<URL> propertyFiles) {
        CommandLineParser parser = new GnuParser();
        try {
            CommandLine cmd = parser.parse(getOptions(), args);
            parseCommandForInfoOptions(cmd);
            parseCommandLineForSystemProperties(cmd);
            parseCommandLineForVerbosity(cmd);
            // Retreive the list of the property files given on CLI
            if (cmd.hasOption('f')) {
                for (String rawFilename : cmd.getOptionValues('f')) {
                    if (rawFilename == null || "".equals(rawFilename)) { //$NON-NLS-1$
                        showHelp();
                    }
                    File file = new File(rawFilename);
                    if (!file.canRead()) {
                        //CHECKSTYLE:OFF
                        System.err.println(Locale.getString("INVALID_PROPERTY_FILENAME", //$NON-NLS-1$
                                rawFilename));
                        //CHECKSTYLE:ON
                        System.exit(ERROR_EXIT_CODE);
                    }
                    propertyFiles.add(file.toURI().toURL());
                }
            }
            return cmd.getArgs();
        } catch (IOException | ParseException e) {
            //CHECKSTYLE:OFF
            e.printStackTrace();
            //CHECKSTYLE:ON
            showHelp();
            // Only to avoid compilation errors
            throw new Error();
        }
    }

    /** Show an error message, and exit.
     *
     * @param message - the description of the error.
     * @param e - the cause of the error.
     */
    protected static void showError(String message, Throwable e) {
        if (message != null && !message.isEmpty()) {
            //CHECKSTYLE:OFF
            System.err.println(message);
            //CHECKSTYLE:ON
        }
        if (e != null) {
            //CHECKSTYLE:OFF
            e.printStackTrace();
            //CHECKSTYLE:ON
        }
        showHelp();
    }

    private static Class<? extends Agent> loadAgentClass(String fullyQualifiedName) {
        Class<?> type;
        try {
            type = Class.forName(fullyQualifiedName);
        } catch (Exception e) {
            showError(Locale.getString("INVALID_AGENT_QUALIFIED_NAME", //$NON-NLS-1$
                    fullyQualifiedName, System.getProperty("java.class.path")), //$NON-NLS-1$
                    null);
            return null;
        }
        // The following test is needed because the
        // cast to Class<? extends Agent> is not checking
        // the Agent type (it is a generic type, not
        // tested at runtime).
        if (Agent.class.isAssignableFrom(type)) {
            return type.asSubclass(Agent.class);
        }

        showError(Locale.getString("INVALID_AGENT_TYPE", //$NON-NLS-1$
                fullyQualifiedName), null);
        return null;
    }

    /** Main function that is parsing the command line and launching
     * the first agent.
     *
     * @param args - command line arguments
     * @see #startJanus(Class, Class, Object...)
     */
    public static void main(String[] args) {
        try {
            List<URL> propertyFiles = new ArrayList<>();
            Object[] freeArgs = parseCommandLine(args, propertyFiles);
            if (JanusConfig.getSystemPropertyAsBoolean(JanusConfig.JANUS_LOGO_SHOW_NAME,
                    JanusConfig.JANUS_LOGO_SHOW)) {
                showJanusLogo();
            }
            if (freeArgs.length == 0) {
                showError(Locale.getString("NO_AGENT_QUALIFIED_NAME"), //$NON-NLS-1$
                        null);
            }
            String agentToLaunch = freeArgs[0].toString();
            freeArgs = Arrays.copyOfRange(freeArgs, 1, freeArgs.length, String[].class);
            // Load the agent class
            Class<? extends Agent> agent = loadAgentClass(agentToLaunch);
            assert (agent != null);
            // Load property files
            Properties systemProperties = System.getProperties();
            for (URL url : propertyFiles) {
                try (InputStream stream = url.openStream()) {
                    systemProperties.load(stream);
                }
            }
            // Set the boot agent classname
            System.setProperty(JanusConfig.BOOT_AGENT, agent.getCanonicalName());

            startJanus(null, (Class<? extends Agent>) agent, freeArgs);
        } catch (Exception e) {
            showError(Locale.getString("LAUNCHING_ERROR", //$NON-NLS-1$
                    e.getLocalizedMessage()), e);
            return;
        }
    }

    /** Replies the command line options supported by this boot class.
     *
     * @return the command line options.
     */
    public static Options getOptions() {
        Option opt;
        Options options = new Options();

        options.addOption("B", "bootid", false, //$NON-NLS-1$//$NON-NLS-2$
                Locale.getString("CLI_HELP_B", //$NON-NLS-1$
                        JanusConfig.BOOT_DEFAULT_CONTEXT_ID_NAME, JanusConfig.RANDOM_DEFAULT_CONTEXT_ID_NAME));

        options.addOption("f", "file", true, //$NON-NLS-1$//$NON-NLS-2$
                Locale.getString("CLI_HELP_F")); //$NON-NLS-1$

        options.addOption("h", "help", false, //$NON-NLS-1$//$NON-NLS-2$
                Locale.getString("CLI_HELP_H")); //$NON-NLS-1$

        options.addOption("nologo", false, //$NON-NLS-1$
                Locale.getString("CLI_HELP_NOLOGO")); //$NON-NLS-1$

        options.addOption("o", "offline", false, //$NON-NLS-1$//$NON-NLS-2$
                Locale.getString("CLI_HELP_O", JanusConfig.OFFLINE)); //$NON-NLS-1$

        options.addOption("q", "quiet", false, //$NON-NLS-1$//$NON-NLS-2$
                Locale.getString("CLI_HELP_Q")); //$NON-NLS-1$

        options.addOption("R", "randomid", false, //$NON-NLS-1$//$NON-NLS-2$
                Locale.getString("CLI_HELP_R", //$NON-NLS-1$
                        JanusConfig.BOOT_DEFAULT_CONTEXT_ID_NAME, JanusConfig.RANDOM_DEFAULT_CONTEXT_ID_NAME));

        options.addOption("s", "showdefaults", false, //$NON-NLS-1$//$NON-NLS-2$
                Locale.getString("CLI_HELP_S")); //$NON-NLS-1$

        options.addOption("v", "verbose", false, //$NON-NLS-1$//$NON-NLS-2$
                Locale.getString("CLI_HELP_V")); //$NON-NLS-1$

        options.addOption("W", "worldid", false, //$NON-NLS-1$//$NON-NLS-2$
                Locale.getString("CLI_HELP_W", //$NON-NLS-1$
                        JanusConfig.BOOT_DEFAULT_CONTEXT_ID_NAME, JanusConfig.RANDOM_DEFAULT_CONTEXT_ID_NAME));
        StringBuilder b = new StringBuilder();
        int l = 0;
        for (String logLevel : LoggerCreator.getLevelStrings()) {
            if (b.length() > 0) {
                b.append(", "); //$NON-NLS-1$
            }
            b.append(logLevel);
            b.append(" ("); //$NON-NLS-1$
            b.append(l);
            b.append(")"); //$NON-NLS-1$
            ++l;
        }
        opt = new Option("l", "log", true, Locale.getString("CLI_HELP_L", //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
                JanusConfig.VERBOSE_LEVEL_VALUE, b));
        opt.setArgs(1);
        options.addOption(opt);
        opt = new Option("D", true, Locale.getString("CLI_HELP_D")); //$NON-NLS-1$//$NON-NLS-2$
        opt.setArgs(2);
        opt.setValueSeparator('=');
        opt.setArgName(Locale.getString("CLI_HELP_D_ARGNAME")); //$NON-NLS-1$
        options.addOption(opt);
        return options;
    }

    /** Show the help message on the standard console.
     * This function never returns.
     */
    public static void showHelp() {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp(Boot.class.getName() + " " //$NON-NLS-1$
                + Locale.getString(Boot.class, "CLI_PARAM_SYNOPTIC"), //$NON-NLS-1$
                getOptions());
        System.exit(ERROR_EXIT_CODE);
    }

    /** Show the default values of the system properties.
     * This function never returns.
     */
    public static void showDefaults() {
        Properties defaultValues = new Properties();
        JanusConfig.getDefaultValues(defaultValues);
        NetworkConfig.getDefaultValues(defaultValues);
        try {
            defaultValues.storeToXML(System.out, null);
        } catch (IOException e) {
            //CHECKSTYLE:OFF
            e.printStackTrace();
            //CHECKSTYLE:ON
        }
        System.exit(ERROR_EXIT_CODE);
    }

    /** Show the heading logo of the Janus platform.
     */
    public static void showJanusLogo() {
        //CHECKSTYLE:OFF
        System.out.println(Locale.getString("JANUS_TEXT_LOGO")); //$NON-NLS-1$
        //CHECKSTYLE:ON
    }

    /** Launch the Janus kernel and the first agent in the kernel.
     * <p>
     * Thus function does not parse the command line.
     * See {@link #main(String[])} for the command line management.
     * When this function is called, it is assumed that all the
     * system's properties are correctly set.
     * <p>
     * The platformModule parameter permits to specify the injection module to use.
     * The injection module is in change of creating/injecting all the components
     * of the platform. The default injection module is retreived from the system
     * property with the name stored in {@link JanusConfig#INJECTION_MODULE_NAME}.
     * The default type for the injection module is stored in the constant
     * {@link JanusConfig#INJECTION_MODULE_NAME_VALUE}.
     *
     * @param platformModule - type of the injection module to use for initializing the platform,
     *                         if <code>null</code> the default module will be used.
     * @param agentCls - type of the first agent to launch.
     * @param params - parameters to pass to the agent as its initliazation parameters.
     * @return the kernel that was launched.
     * @throws Exception - if it is impossible to start the platform.
     * @see #main(String[])
     */
    public static Kernel startJanus(Class<? extends Module> platformModule, Class<? extends Agent> agentCls,
            Object... params) throws Exception {
        // Get the start-up injection module
        Class<? extends Module> startupModule = platformModule;
        if (startupModule == null) {
            startupModule = JanusConfig.getSystemPropertyAsClass(Module.class, JanusConfig.INJECTION_MODULE_NAME,
                    JanusConfig.INJECTION_MODULE_NAME_VALUE);
        }
        assert (startupModule != null) : "No platform injection module"; //$NON-NLS-1$
        Kernel k = Kernel.create(startupModule.newInstance());
        if (LoggerCreator.getLoggingLevelFromProperties().intValue() > 0) {
            //CHECKSTYLE:OFF
            System.out.println(Locale.getString("LAUNCHING_AGENT", agentCls.getName())); //$NON-NLS-1$
            //CHECKSTYLE:ON
        }
        k.spawn(agentCls, params);
        return k;
    }

}