4.3. Configuration and Startup

IRIS uses the Spring Framework for service loading and initialization. Service loading occurs in the following stages:

  1. Bootstrapping is done first by loading the Spring configuration file iris/bin/iris_svc_config_bootstrap.xml. The main goal of bootstrapping is to load the base configuration file and start as fast as possible, only loading the minimum amount of libraries and configuration files before displaying the login screen. The boostrap configuration file provides the following functionality:

    • Loads Keychain services used by IRIS at startup, exposed through the bean "com.sri.iris.plugins.keychain.bootstrap.ApplicationContext". The keychain server is automatically started if IRIS cannot ping it.

    • Defines "BootstrapProperties" which is only used to allow access to Java system properties. IRIS loads the file "iris/bin/iris.properties" at startup into the Java system properties (before loading the bootstrap config). Note that this properties file is passed in as an argument in iris.bat (-p argument), and it contains the following properties:

      • iris.plugins.dir=../plugins: points to the location where IRIS plugins are loaded

      • iris.data.dir=../data: the location of the IRIS Data directory which contains ontology files.

      • iris.serviceConfig=${iris.bin.dir}/iris_svc_config_full.xml This property tells IRIS what service configuration to load.

      • These are the base properties, but any more specified here will be automatically added to the Java system properties and also be made available to Spring beans.

    • Loads com.sri.iris.StartupOptionsContext which loads the options IRIS presents to the user for different startup configurations.

    • The following basic IRIS services are loaded:

  2. The login screen is displayed using the service com.sri.iris.kernel.user.ILogin. Once the user has entered the login information, the main IRIS service config is loaded by Spring. The main service config is specified in the iris.properties file as specified above. The default is iris_svc_config_full.xml.

  3. The login screen a combo box used to control what configuration IRIS starts in:

    • The combo box allows the user to define a configuration for running IRIS. For example "remote services only" or "Everything".

    As mentioned above, the bean com.sri.iris.StartupOptionsContext specifies beans that are loaded to populate the startup options. The bean definition is:

        <bean id="com.sri.iris.StartupOptionsContext"
              class="org.springframework.context.support.FileSystemXmlApplicationContext"
              autowire="no" dependency-check="none" init-method="refresh" lazy-init="true">
            <constructor-arg>
                <list>
                    <value>${iris.bin.dir}/**/*-startupOptions.xml</value>
                    <value>${iris.bin.dir}/**/*-beanNames.xml</value>
                    <value>${iris.plugins.dir}/*/*-startupOptions.xml</value>
                    <value>${iris.plugins.dir}/*/*-login-handler*.xml</value>
                </list>
            </constructor-arg>
            <constructor-arg>
                <ref bean="com.sri.iris.bootstrap.ApplicationContext"/>
            </constructor-arg>
            <property name="classLoader">
                <ref bean="com.sri.iris.ClassLoader"/>
            </property>
        </bean>

    In the current IRIS system this tells the bootstrap process to load the following files. Note that below the source locations are given. The files are actually loaded from the plugins directory and are copied there during the build:

    iris/bin/config/startup/iris-beanNames.xml

    Provides aggregate beans that can be referenced in startup option files.

    iris/bin/config/startup/standAlone-startupOptions.xml

    The default startup options, or the "Full Configuration"

    **/*-startupOptions.xml in iris/bin and iris/plugins

    Plugins can define their own startup options files. The options wil be loaded and presented only if the plugin is present. IRIS also comes with some default startup options.

    iris/plugins-src/proxy/bin/aio-startupOptions.xml

    Instructs IRIS to present an aggregate option for "IRIS All In One"

    iris/plugins-src/proxy/bin/proxy-login-handlers-ctx.xml

    Defines instances of com.sri.iris.ui.widgets.ILoginHandler for the IRIS modes: "IRIS All In One". The login handlers fires immediately before the main service config is loaded. Currently special handling is done for each mode as follows:

    IRIS All In One

    com.sri.iris.remote.proxy.IrisAllInOneLoginHandler: Since IRIS will be running all in one VM, this login handler exists to set system properties that optimize the runtime performance of IRIS.

    These login handlers are all loaded through the configuration file iris/plugins-src/proxy/bin/proxy-login.xml which is laoded from the application context in iris/plugins-src/proxy/bin/proxy-login-handlers-ctx.xml

  4. After the user loads IRIS and the login handlers fire, the main service config file is loaded. This is usually iris/bin/iris_svc_config_full.xml but can change according to the documentation above (if it is different in iris.properties or the user is running IRIS Client). IRIS proceeds to scan the service config, recuresively loading all ApplicationContext instances it finds from the Spring config. IRIS then assembles all the bean names found in the files and matches those beans with the ones requested for loading in the startup options. For performance reasons, IRIS caches the bean information in the file iris/dest/appContexts_cache.obj because on first time start up this can take a while.[1]

The next sections give more documentation on writing startup option files and conventions.

4.3.1. Configuration Conventions

IRIS uses the following conventions in the Spring initialization files, and anyone who modifies or creates bean configuration must also follow the same conventions:

  • All beans are lazy inited. This is required for the simple reason that there is not a compelling reason not to lazy init. IRIS takes advantage of lazy initialization in many ways and it is assumed beans will always be lazy inited.

  • Bean dependencies must be interfaces, and all interfaces must start with a capital I followed by the interface. For example, if there is a bean with class Foo and another bean names Bar needs to depend on Foo, then the interface IFoo must be created so that Bar can access Foo through the interface. Note that the dependency must also be specified to reference IFoo interface name (see the requirement below). This is possible to do in Spring in many ways. For example, the Foo bean can have its name or id set to the IFoo interface class name, or the IFoo bean can be an alias for Foo, it can be created using Foo as a factory.

  • For interfaces, bean names are the interface name. For example, if the bean com.sri.Foo exposes the interface com.sri.IFoo, then "com.sri.IFoo" is the name used for dependencies using Spring's ref/bean syntax.

  • In the IRIS service configuration file many beans are set to "autowire", meaning that the bean dependencies do not need to be declared. This is important to keep in mind because it implies that only one instance of each of the core IRIS beans can exist. There are many other reasons why this must be the case, and therefore it is not possible to define another IRIS bean with the same name/id as an existing bean.

  • Beans should implement com.sri.iris.kernel.serviceloader.IPlugin and furthermore must set the lifecycle attributes "init-method" to "startup" and "destroy-method" to "shutdown". This is not strictly enforced; IRIS will run fine if the bean does not implement IPlugin or has different lifecycle methods. However, as a convention beans should implement IPlugin as a standard way of defining the lifecycle methods for the bean.

  • Bean names/ids should be unique among all IRIS plugins. This is because IRIS flattens the application contexts to allow quick searching when loading startup options. This rule is not strictly enforced, and IRIS will still work with duplicate bean names. However, for top-level service-oriented beans their name should be unique among all IRIS plugin beans.

4.3.2. Startup Options

As mentioned before, startup options are a way to specify that only a subset of beans load when IRIS starts. A plugin can provide startup options by simply including a file with the name syntax "NAME-startupOptions.xml" in its plugin directory (under iris/plugins). An example file is:

<beans default-lazy-init="true">
    <bean id="StartupOptions-StandAlone" class="com.sri.iris.kernel.StartupOptions" name="defaultOptions">
        <property name="displayName" value="Full Configuration" />
        <property name="description" value="Run IRIS with all installed plugins" />
        <property name="defaultOptions" value="true" />
        <property name="includedPluginBeanNames">
            <value>**</value>
        </property>
        <property name="excludedPluginBeanNames">
            <list>
                <value>MailLoader</value>
            </list>
        </property>
    </bean>
</beans>

This is a Spring configuration file where an instance of com.sri.iris.kernel.StartupOptions is declared which can have the following properties:

displayName

String, required: The name displayed in the combo box on the login screen

description

String, required: Displayed below the combo boxes when the options are selected

defaultOptions

true/false, optional (default false): whether or not this should be the default options selected. Note that IRIS remembers the selection after restarting (it is stored in the keychain server), so this only applies the first time IRIS is started. Since IRIS already provides a default startup options, plugin startup options should set this to false.

includedPluginBeanNames

List<String>, required: This can be "**" indicating all beans, or it can be a list of bean names. The bean names can be beans in the core IRIS service config or beans in plugin configs. Note that no special wildcard syntax is supported when specifying beans names (ie, only "**" is supported).

excludedPluginBeanNames

List<String>, optional: This is a list of bean names. The bean names can be beans in the core IRIS service config or beans in plugin configs. Note that no special wildcard syntax is supported when specifying beans names

loginHandlerBeanName

String, optional: if this is specified, then it should be the name of a bean which implements com.sri.iris.ui.widgets.ILoginHandler. IRIS will search for this bean in the bootstrap config, and IRIS will search through application contexts recursively in the bootstrap config. The convention for declaring login handler beans is to provide a file name of the format "*-login-handler*.xml". An example of this is shown in the next section.

Specifying startup options as described above will cause them to appear in the bottom cmbo box at startup. Startup options which appear in the top combo box are called aggregate startup options and are instance of com.sri.iris.kernel.AggregableStartupOptions. These startup options take the same parameters as regular startup options with additional arguments:

allowAnyOptions

true/false, required: This is usually set to true, meaning the startup options can aggregate with any other options and the user should be able to select this aggregate options from the top list and another option from the bottom list. If this is false, then the aggregate parameter must be set.

aggregate

com.sri.iris.kernel.StartupOptions, optional: This is only used if allowAnyOptions is false. It specifies a hardwired aggregate for these options.

IRIS aggregates options by combining the included and excluded beans. See the method applyAggregateOption in com.sri.iris.kernel.StartupOptions for more details.

4.3.3. IRIS exec scripts

IRIS is executed by running iris.bat or iris.sh from iris/bin. These scripts use the environment variable JAVA_HOME to launch Java, and they pass the following Java system and application properties when launching Java:

-Djava.security.manager -Djava.security.policy=iris.security.policy

Standard Java security settings, providing a security policy file

-Diris.cacheAppContextList=true

Whether or not to cache the app context lists on startup which improves performance

-Diris.keychain.launch=true

Specifies whether or not IRIS should auto-launch the keychain server if the keychain server cannot be pinged successfully

-Djena.faster=yes

This is a JENA-specific parameter which tells JENA to use the faster memory graphs

-DautoDeleteLuceneLock=true

Tells IRIS whether or not to auto-delete the Lucene database lock files if they are locked. IRIS already detects whether or not another application is using the databases, so it is safe to auto-delete the lock files. If this parameter is false, the user will be prompted if the lock files indicate the Lucene databases are in use.

-Djena.hashing=yes

This is a JENA-specific parameter which improves performance

-Djavax.xml.parsers.DocumentBuilderFactory=... -Djavax.xml.parsers.SAXParserFactory=...

Sets XML parameters for IRIS compatibility

-Dsun.java2d.noddraw=true

Fixes a Swing direct-draw bug.

-Xmx512M -XX:MaxPermSize=156m

IRIS needs at least 512M of memory and a large perm gen space

-Dlog4j.configuration=file:iris_log_config.xml

IRIS uses log4j for all logging

com.sri.iris.bootstrap.Main

This is the initial main class. The intent of this class is to accept classpath parameters and other Java VM-setup parameters. This class in turn calls the IRIS main class com.sri.iris.kernel.Main.

-l iris_log_config.xml

The log config is passed in twice, once as a Java parameter and again as an argument. The Java system property is set because it is a standard property and any third-party libraries that set up log4j will then use the IRIS log config.

-s "iris_svc_config_bootstrap.xml"

The initial Spring configuration to load.

-p %IRIS_PROPS%

This is the IRIS properties file to load. The property file is iris.properties, but it can be changed via arguments to the script file (see below).

It is also possible to pass in runtime arguments to IRIS as arguments to iris.bat or iris.sh:

-debug

Tells IRIS to run in Debug mode using the arguments "-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"

IRIS_PROP_MODE

THis system property controls what file to load. For example, the following runs IRIS using a minimal configuration (iris/bin/iris-minimal.bat):

set IRIS_PROP_MODE=.minimal
set JAVA_ARG=-Dproxy.loadClassProps=false
call iris.bat

4.3.4. Bootstrapping IRIS programatically

APIs exist to bootstrap IRIS. These APIs are used heavily in unit tests, and are also available if applications want to bootstrap and use IRIS services. Bootstrapping is often desireable because IRIS can be started very quickly and services are lazy inited. The application can then simply retrieve the services it cares about to perform some task (such as unit testing).

To bootstrap IRIS from another application, note the following constraints:

  • The jar files in iris/dest/jar and iris/lib need to be in the classpath. The plugin jar files do not need to be in the classpath, however because the plugin classpaths are already set up through Spring.

  • It is best to always run the application from iris/bin. It is possible to run the application in another directory, but this is not tested well (some pointers are given below).

  • Note that the IRIS exec scripts set various system properties at startup. It is not required to set these properties

  • The keychain server should be running (it is required for most services). The application can set a system property to tell IRIS to auto-launch the keychain server.

  • The IRIS knowledge base set of services detects if databases are already in use. This means IRIS cannot be running or else these services will fail to load.

  • When bootstrapping IRIS, applications can inject service implementations to change functionality. For example, overiding login behavior so no login is displayed, or chaning the application data directory. An example is shown below.

The main boottrapping interface is com.sri.iris.kernel.IIrisMain. To retrieve an instance of IIrisMain, use the static method "newIrisMain" in com.sri.iris.kernel.Main. An example of bootstrapping IRIS is the base class used to bootstrap for the purpose of unit testing: com.sri.iris.test.IrisJUnitTestCase. The following code boostraps IRIS (copied from IrisJUnitTestCase):

    // It is recommended to disable caching of classes when bootstrapping
    System.setProperty("proxy.loadClassProps", "false");
    main = Main.newIrisMain("iris_svc_config_bootstrap.xml", false, "iris.properties");
    final Object loadLock = new Object();
    if (!launchExternalKeychainServer) {
        // Make the keychain server run locally, and also make it bind to a random port...
        System.setProperty("iris.keychain.launch", "false");
        System.setProperty("iris.keychain.launch.checkIsRunning", "false");
        System.setProperty("keychain.setlocal", "true");
        System.setProperty("keychain.port", "0"); // 0 forces a random port to be used
        options.getIncludedPluginBeanNames().add("keychain_server");
    }

Note the following:

  • By default IRIS auto-launches the keychain server. Most unit tests do not want to do this. So, the code in the if block above causes the keychain to be launched in the same Java VM. Note also that the keychain port is set to 0 which indicates to choose the first avilable port. This allows the keychain to run even if another keychain server is already running. Then any applications in IRIS which access the keychain will use the port that was chosen (because they use the internal IKeychain service in IRIS).

  • The application can include startup options when bootstrapping IRIS. By default all beasn are lazy inited, so an empty startup options can be specified and then services retrieved directly from the Spring application context.

  • The iris.properties file is specified when creating the IIrisMain instance. The application can specify its own properties file that gives the locations of the IRIS plugins, data and bin directories.

    Note that bootstrapping from a location pother than iris/bin is not tested well and may not work.

IRIS is started asynchronously. Once IRIS is started, services can be injected or retrieved. The following code adds a startup listener, starts IRIS, and during startup injects services.

    main.addStartupListener(new IIrisStartupListener() {
        public void notifyRootApplicationContextCreated(ApplicationContext rootContext) {
            IrisJUnitTestCase.this.rootContext = rootContext;
            if (!(rootContext instanceof AbstractApplicationContext)) {
                throw new RuntimeException("Expected root context to be of type AbstractApplicationContext");
            }
            // Temporary IRIS data dirs are used for the test. Also, a dummy username/password is supplied
            // and ILogin is overriden so no login pane is displayed.
            ((AbstractApplicationContext)rootContext).getBeanFactory().registerSingleton(
                    IUserAccountManager.class.getName(), new IUserAccountManager() {
                    public void createAccount(String username, String password) {}
                    public boolean userAccountExists() { return false; }
                    public String getUserName() { return loginName; }
                    public String getPassword() { return loginPassword; }
            });
            ((AbstractApplicationContext)rootContext).getBeanFactory().registerSingleton(
                    ILogin.class.getName(), new ILogin() {
                    private List<ILoginListener> listeners = new ArrayList<ILoginListener>();

                    public void addLoginListener(ILoginListener listener) {
                        listeners.add(listener);
                    }

                    public void displayLogin() {
                        // Immediately indicate user has logged in
                        new Thread(new Runnable() {
                            public void run() {
                                for (ILoginListener listener : listeners) {
                                    listener.notifyLoggedIn(options);
                                }
                            }
                        }).start();
                    }

                    public void hideLogin() {}
                    public void setNumbeans(int nBeans) {}
                    public void notifyBeanStartLoading(String beanName) { }
                    public void notifyBeanFinishedLoading(String beanName) {}
                    public void updateStatus(String status) {}
                    public void setStartupOptions(List<StartupOptions> options) {}
            });
            ((AbstractApplicationContext)rootContext).getBeanFactory().registerSingleton(
                    IIrisAppDirLocator.class.getName(), new TestIrisAppDirLocator(appDirRoot));
        }

        public void notifyIRISLoadStarted(StartupOptions startupOptions) {}
        public void notifyApplicationContextCreated(ApplicationContext appContext, String beanName) {}
        public void notifyBeanLoaded(ApplicationContext appContext, String beanName, Object bean) {}
        public void notifyIRISFailedToLoad(Exception ex) {}
        public void setStartupOptions(List<StartupOptions> options) {}

        public void notifyIRISLoaded(Map<String, Throwable> loadErrors) {
            irisSystem = (IIrisSystem)rootContext.getBean(IIrisSystem.class.getName());
            // IRIS has been loaded
            synchronized (loadLock) {
                loadLock.notifyAll();
            }
        }
    });
    // Load IRIS and wait for it to load
    long startTime = System.currentTimeMillis();
    main.startIris();
    main.getSecurityManager().setEnforceShutdown(false);

Note the following:

  • The method notifyRootApplicationContextCreated is called by IRIS before IRIS loads services. At this point services can be injected to overide default IRIS services. The above code calls registerSingleton on the Spring application contrext to override the user account manager, login and appp directory locator.

  • IRIS calls notifyIRISLoaded when IRIS has finished loading.

  • By default IRIS does not allow Runtime.exit to be called (enforce through a security manager). THis is due to legacy code that has been integrated into the CALO system which calls Runtime.exit. The application can override this behavior by calling main.getSecurityManager().setEnforceShutdown(false);.

Finally, note that bootstrapping programatically is only currently used for unit testing. However, the same technique applies to any application. For example, it is possible to write a small test application that bootstraps the knowledge base and runs some queries.

4.3.5. Debugging IRIS

To debug IRIS, run "iris.bat -debug" or "iris.sh -debug". Then configure the debugger to connect to port 5005 on the machine IRIS is running.



[1] Note that IRIS only scans the service configs so that it can present a progress bar at startup. In general, when doing development it is a good idea to delete appContexts_cache.obj when swapping a plugin (this happens automatically if a clean build is performed)