org.lilyproject.runtime.LilyRuntime.java Source code

Java tutorial

Introduction

Here is the source code for org.lilyproject.runtime.LilyRuntime.java

Source

/*
 * Copyright 2013 NGDATA nv
 * Copyright 2007 Outerthought bvba and Schaubroeck nv
 *
 * 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 org.lilyproject.runtime;

import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.jci.monitor.FilesystemAlterationMonitor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.lilyproject.runtime.classloading.ClassLoaderBuilder;
import org.lilyproject.runtime.classloading.ClasspathEntry;
import org.lilyproject.runtime.conf.Conf;
import org.lilyproject.runtime.configuration.ConfManager;
import org.lilyproject.runtime.model.ConfigError;
import org.lilyproject.runtime.model.LilyRuntimeModel;
import org.lilyproject.runtime.model.LilyRuntimeModelBuilder;
import org.lilyproject.runtime.model.ModuleDefinition;
import org.lilyproject.runtime.model.SourceLocations;
import org.lilyproject.runtime.module.Module;
import org.lilyproject.runtime.module.ModuleConfig;
import org.lilyproject.runtime.module.build.ModuleBuilder;
import org.lilyproject.runtime.module.build.ModuleConfigBuilder;
import org.lilyproject.runtime.module.javaservice.JavaServiceManager;
import org.lilyproject.runtime.rapi.ConfRegistry;
import org.lilyproject.runtime.rapi.Mode;
import org.lilyproject.runtime.repository.ArtifactNotFoundException;
import org.lilyproject.runtime.repository.ArtifactRepository;
import org.lilyproject.runtime.source.ModuleSourceManager;
import org.lilyproject.util.ArgumentValidator;
import org.lilyproject.util.Version;

/**
 * This is the main entry point of the LilyRuntime.
 *
 * <p>Basic usage:
 * <ul>
 *   <li>Create a {@link LilyRuntimeSettings}
 *   <li>Use {@link #LilyRuntime(LilyRuntimeSettings)}
 *   <li>Call {@link #start()}
 * </ul>
 */
public class LilyRuntime {
    private LilyRuntimeSettings settings;
    private LilyRuntimeModel model;
    private ClassLoader rootClassLoader;
    private List<Module> modules;
    private Map<String, Module> modulesById = new HashMap<String, Module>();
    private List<ModuleConfig> moduleConfigs;
    private JavaServiceManager javaServiceManager;
    private ModuleSourceManager moduleSourceManager;

    private enum LifeCycle {
        NOT_STARTED, STARTED, STOPPED
    }

    private LifeCycle state = LifeCycle.NOT_STARTED;
    private Mode mode = Mode.getDefault();
    private FilesystemAlterationMonitor fam = new FilesystemAlterationMonitor();

    protected final Log infolog = LogFactory.getLog(INFO_LOG_CATEGORY);

    public static final String INFO_LOG_CATEGORY = "org.lilyproject.runtime.info";
    public static final String CLASSLOADING_LOG_CATEGORY = "org.lilyproject.runtime.classloading-info";
    public static final String CLASSLOADING_REPORT_CATEGORY = "org.lilyproject.runtime.classloading-report";

    public LilyRuntime(LilyRuntimeSettings settings) {
        ArgumentValidator.notNull(settings, "settings");

        if (settings.getRepository() == null) {
            throw new LilyRTException("LilyRuntimeSettings should contain an artifact repository.");
        }

        if (settings.getConfManager() == null) {
            throw new LilyRTException("LilyRuntimeSettings should contain a ConfManager.");
        }

        this.settings = settings;

        this.javaServiceManager = new JavaServiceManager();
        this.moduleSourceManager = new ModuleSourceManager(fam);
    }

    public Mode getMode() {
        return mode;
    }

    public void setMode(Mode mode) {
        if (state.ordinal() > LifeCycle.NOT_STARTED.ordinal()) {
            throw new IllegalStateException("Runtime mode canot be changed once started.");
        }

        this.mode = mode;
    }

    public LilyRuntimeModel buildModel() {
        // Init the configuration manager
        ConfManager confManager = settings.getConfManager();
        confManager.initRuntimeConfig();

        ConfRegistry confRegistry = confManager.getRuntimeConfRegistry();

        return buildModel(confRegistry);
    }

    private LilyRuntimeModel buildModel(ConfRegistry confRegistry) {
        LilyRuntimeModel newModel;
        if ((settings.getModel() != null)) {
            newModel = settings.getModel();
        } else {
            Conf modulesConf = confRegistry.getConfiguration("wiring", false, false);
            Set<String> disabledModuleIds = settings.getDisabledModuleIds() != null
                    ? settings.getDisabledModuleIds()
                    : Collections.<String>emptySet();
            SourceLocations sourceLocations = settings.getSourceLocations() != null ? settings.getSourceLocations()
                    : new SourceLocations();
            try {
                newModel = LilyRuntimeModelBuilder.build(modulesConf, disabledModuleIds, settings.getRepository(),
                        sourceLocations);
            } catch (Exception e) {
                throw new LilyRTException("Error building the Lily model from configuration.", e);
            }
        }
        return newModel;
    }

    /**
     * Starts the Lily Runtime. This will launch all modules (i.e. their Spring containers),
     * and set up the restservices.
     *
     * <p>A LilyRuntime instance can only be started once, even after it has been stopped.
     * Just create a new LilyRuntime instance with the same LilyRuntimeConfig if you want
     * to (re)start another instance.
     */
    public void start() throws LilyRTException, MalformedURLException, ArtifactNotFoundException {
        // a LilyRuntime object cannot be started twice, even if it has been stopped in between
        if (state.ordinal() > LifeCycle.NOT_STARTED.ordinal()) {
            throw new LilyRTException("This Lily Runtime instance has already been started before.");
        }
        state = LifeCycle.STARTED;

        // Init the configuration manager
        ConfManager confManager = settings.getConfManager();
        confManager.initRuntimeConfig();

        ConfRegistry confRegistry = confManager.getRuntimeConfRegistry();

        this.model = buildModel(confRegistry);

        // Validate the config
        List<ConfigError> configErrors = new ArrayList<ConfigError>();
        model.validate(configErrors);
        if (configErrors.size() > 0) {
            StringBuilder errorMsg = new StringBuilder();
            String subject = configErrors.size() == 1 ? "error" : "errors";
            errorMsg.append("Encountered the following ").append(subject).append(" in the runtime configuration: ");
            for (int i = 0; i < configErrors.size(); i++) {
                if (i > 0) {
                    errorMsg.append(", ");
                }
                errorMsg.append(configErrors.get(i).getMessage());
            }
            throw new LilyRTException(errorMsg.toString());
        }

        moduleConfigs = new ArrayList<ModuleConfig>();

        // First read the configuration of each module, and do some classpath checks
        if (infolog.isInfoEnabled()) {
            infolog.info("Reading module configurations of " + model.getModules().size() + " modules.");
        }
        for (ModuleDefinition entry : model.getModules()) {
            if (infolog.isInfoEnabled()) {
                infolog.debug("Reading module config " + entry.getId() + " - " + entry.getFile().getAbsolutePath());
            }
            ModuleConfig moduleConf = ModuleConfigBuilder.build(entry, this);
            moduleConfigs.add(moduleConf);
        }

        // Check / build class path configurations
        Conf classLoadingConf = confRegistry.getConfiguration("classloading");
        List<ClasspathEntry> sharedClasspath = ClassLoaderConfigurer.configureClassPaths(moduleConfigs,
                settings.getEnableArtifactSharing(), classLoadingConf);

        // Construct the shared classloader
        infolog.debug("Creating shared classloader");

        rootClassLoader = ClassLoaderBuilder.build(sharedClasspath, this.getClass().getClassLoader(),
                settings.getRepository());

        // Construct the classloaders of the various modules
        List<ClassLoader> moduleClassLoaders = new ArrayList<ClassLoader>();
        for (ModuleConfig cfg : moduleConfigs) {
            ClassLoader classLoader = cfg.getClassLoadingConfig().getClassLoader(getClassLoader());
            moduleClassLoaders.add(classLoader);
        }

        // Initialize the ConfManager for the modules configuration
        confManager.initModulesConfig(moduleConfigs);

        // Create the modules
        infolog.info("Starting the modules.");

        modules = new ArrayList<Module>(model.getModules().size());
        for (int i = 0; i < moduleConfigs.size(); i++) {
            ModuleConfig moduleConfig = moduleConfigs.get(i);
            Module module = ModuleBuilder.build(moduleConfig, moduleClassLoaders.get(i), this);
            modules.add(module);
            modulesById.put(module.getDefinition().getId(), module);
        }

        // Start the FAM, conf manager refreshing
        fam.start();
        confManager.startRefreshing();

        infolog.info("Runtime initialisation finished.");
    }

    public List<ModuleConfig> getModuleConfigs() {
        return moduleConfigs;
    }

    public ArtifactRepository getArtifactRepository() {
        return settings.getRepository();
    }

    public ClassLoader getClassLoader() {
        return rootClassLoader;
    }

    public JavaServiceManager getJavaServiceManager() {
        return javaServiceManager;
    }

    public ModuleSourceManager getModuleSourceManager() {
        return moduleSourceManager;
    }

    public List<Module> getModules() {
        return modules;
    }

    public Module getModuleById(String moduleId) {
        return modulesById.get(moduleId);
    }

    public LilyRuntimeSettings getSettings() {
        return settings;
    }

    public LilyRuntimeModel getModel() {
        return model;
    }

    public void stop() {
        if (state != LifeCycle.STARTED) {
            throw new LilyRTException(
                    "Cannot stop the runtime, it is in state " + state + " instead of " + LifeCycle.STARTED);
        }

        // TODO temporarily disabled because FAM.stop() is slow
        // See JCI jira patch: https://issues.apache.org/jira/browse/JCI-57 (the newer one in commons-io has the same problem)
        // fam.stop();
        // Added the following workaround:
        if (System.getSecurityManager() == null) {
            try {
                Field famRunningField = fam.getClass().getDeclaredField("running");
                famRunningField.setAccessible(true);
                Field threadField = fam.getClass().getDeclaredField("thread");
                threadField.setAccessible(true);
                Thread famThread = (Thread) threadField.get(fam);
                if (famThread != null) {
                    famRunningField.setBoolean(fam, false);
                    famThread.interrupt();
                    fam.stop();
                }
            } catch (Exception e) {
                infolog.error("Error stopping FilesystemAlterationMonitor", e);
            }
        } else {
            infolog.warn(
                    "Unable to stop the FilesystemAlterationMonitor using workaround since a security manager is installed.");
        }

        // In case starting the runtime failed, modules might be null
        if (modules != null) {
            infolog.info("Shutting down the modules.");
            List<Module> reversedModules = new ArrayList<Module>(this.modules);
            Collections.reverse(reversedModules);
            this.modules = null;
            this.javaServiceManager.stop();

            for (Module module : reversedModules) {
                try {
                    module.shutdown();
                } catch (Throwable t) {
                    infolog.error("Error shutting down module " + module.getDefinition().getId(), t);
                }
            }
        }

        settings.getConfManager().shutdown();

    }

    public static String getVersion() {
        return Version.readVersion("org.lilyproject", "lily-runtime");
    }

    public ConfManager getConfManager() {
        return settings.getConfManager();
    }

}