All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ninja.Bootstrap Maven / Gradle / Ivy

There is a newer version: 7.0.0
Show newest version
/**
 * Copyright (C) 2012-2016 the original author or authors.
 *
 * 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 ninja;

import java.util.ArrayList;
import java.util.List;

import ninja.application.ApplicationRoutes;
import ninja.lifecycle.LifecycleSupport;
import ninja.logging.LogbackConfigurator;
import ninja.scheduler.SchedulerSupport;
import ninja.utils.NinjaConstant;
import ninja.utils.NinjaProperties;
import ninja.utils.NinjaPropertiesImpl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Singleton;
import com.google.inject.Stage;
import ninja.conf.FrameworkModule;
import ninja.conf.NinjaBaseModule;
import ninja.conf.NinjaClassicModule;

/**
 * Bootstrap for a Ninja application.  Assists with initializing logging,
 * configuring Guice injector, applying user-defined Guice modules/bindings,
 * creates the injector, and compiles the routes.
 * 
 * Subclasses will likely want to provide an inherited configure() method that
 * adds modules specific to the subclassed Bootstrap.  See ninja-servlet
 * and NinjaServletContext for an example of a subclass.
 */
public class Bootstrap {
    static private final Logger logger = LoggerFactory.getLogger(Bootstrap.class);

    static public final String APPLICATION_GUICE_MODULE_CONVENTION_LOCATION = "conf.Module";
    static public final String APPLICATION_GUICE_SERVLET_MODULE_CONVENTION_LOCATION = "conf.ServletModule";
    static public final String ROUTES_CONVENTION_LOCATION = "conf.Routes";
    static public final String NINJA_CONVENTION_LOCATION = "conf.Ninja";

    private final NinjaPropertiesImpl ninjaProperties;
    private final List modulesToLoad;
    private final Optional applicationModulesBasePackage;
    private Injector injector = null;

    public Bootstrap(NinjaPropertiesImpl ninjaProperties) {
        Preconditions.checkNotNull(ninjaProperties);
        this.ninjaProperties = ninjaProperties;
        this.modulesToLoad =  new ArrayList<>();
        
        // custom base package for application modules (e.g. com.example.conf.Routes)
        this.applicationModulesBasePackage
            = Optional.fromNullable(ninjaProperties.get(
                    NinjaConstant.APPLICATION_MODULES_BASE_PACKAGE));
        
    }

    public synchronized void boot() {
        // 1. initialize logging
        initLogback();

        if (this.injector != null) {
            throw new RuntimeException("Bootstrap already booted");
        }

        // 2. configure all modules for injector
        try {
            configure();
        } catch (Exception e) {
            throw new RuntimeException("Unable to configure Ninja", e);
        }
        
        // 3. create injector
        long startTime = System.currentTimeMillis();

        try {
            initInjector();
        } catch (Exception e) {
            throw new RuntimeException("Ninja injector cannot be generated. Please check log for further errors.", e);
        }
        
        long injectorStartupTime = System.currentTimeMillis() - startTime;
        logger.info("Ninja injector started in " + injectorStartupTime + " ms.");
        
        // 4. initialize routes
        try {
            initRoutes();
        } catch (Exception e) {
            throw new RuntimeException("Unable to initialize Ninja routes", e);
        }
        
        // 5. framework started!
        Ninja ninja = injector.getInstance(Ninja.class);
        ninja.onFrameworkStart();
    }

    public synchronized void shutdown() {
        if (this.injector != null) {
            Ninja ninja = injector.getInstance(Ninja.class);
            ninja.onFrameworkShutdown();
            injector = null;
        } else {
            logger.error("Shutdown of Ninja not clean => injector already null.");
        }
    }
    
    public Injector getInjector() {
        return this.injector;
    }
    
    public void addModule(Module module) {
        this.modulesToLoad.add(module);
    }
    
    protected void configure() throws Exception {

        // Base configuration of Ninja
        addModule(new NinjaBaseModule(ninjaProperties));
        
        // Main application module (conf.Module or com.example.conf.Module)
        String applicationModuleClassName
                = resolveApplicationClassName(APPLICATION_GUICE_MODULE_CONVENTION_LOCATION);

        AbstractModule applicationModule = null;
        
        if (doesClassExist(applicationModuleClassName)) {
            Class applicationModuleClass = Class
                    .forName(applicationModuleClassName);

            // Tries to instantiate module by giving the NinjaProperties as constructor arg
            try {
                applicationModule = (AbstractModule) applicationModuleClass
                        .getConstructor(NinjaProperties.class).newInstance(ninjaProperties);
            } catch (NoSuchMethodException e) {
                applicationModule = (AbstractModule) applicationModuleClass
                        .getConstructor().newInstance();
            }
        }
        
        // Slipstream in the "classic" ninja configuration?
        if (applicationModule == null || !(applicationModule instanceof FrameworkModule)) {
            // Classic configuration of Ninja
            logger.info("Enabling Ninja classic configuration");
            addModule(new NinjaClassicModule(ninjaProperties));
        }
        
        if (applicationModule != null) {
            addModule(applicationModule);
        }
        
        // Ninja module
        String applicationNinjaClassName
                = resolveApplicationClassName(NINJA_CONVENTION_LOCATION);

        final Class ninjaClass;

        if (doesClassExist(applicationNinjaClassName)) {

            final Class clazzPotentially = Class.forName(applicationNinjaClassName);

            if (Ninja.class.isAssignableFrom(clazzPotentially)) {

                ninjaClass = (Class) clazzPotentially;

            } else {

                final String ERROR_MESSAGE = String.format(
                        "Found a class %s in your application's conf directory."
                        + " This class does not implement Ninja interface %s. "
                        + " Please implement the interface or remove the class.",
                        applicationNinjaClassName,
                        Ninja.class.getName());

                logger.error(ERROR_MESSAGE);

                throw new IllegalStateException(ERROR_MESSAGE);

            }

        } else {

           ninjaClass = NinjaDefault.class;

        }

        addModule(new AbstractModule() {
            @Override
            protected void configure() {
                bind(Ninja.class).to(ninjaClass).in(Singleton.class);
            }
        });
    }
    
    private void initInjector() throws Exception {
        // Let the injector generate all instances and stuff
        this.injector = Guice.createInjector(Stage.PRODUCTION, modulesToLoad);
    }
    
    public void initRoutes() throws Exception {
        String applicationRoutesClassName
                = resolveApplicationClassName(ROUTES_CONVENTION_LOCATION);

        if (doesClassExist(applicationRoutesClassName)) {

            Class clazz = Class.forName(applicationRoutesClassName);
            
            ApplicationRoutes applicationRoutes = (ApplicationRoutes) injector
                    .getInstance(clazz);

            Router router = this.injector.getInstance(Router.class);

            applicationRoutes.init(router);
            
            router.compileRoutes();
        }
    }
    
    private void initLogback() {
        // init logging at the very very top
        try {
            Class.forName("ch.qos.logback.classic.joran.JoranConfigurator");
            LogbackConfigurator.initConfiguration(ninjaProperties);
            logger.info("Successfully configured Logback.");
             // It is available
        } catch (ClassNotFoundException exception) {
            logger.info("Ninja did not configure any logging because Logback is not on the classpath (you are probably using slf4j-jdk14).");
        }
    }
    
    protected boolean doesClassExist(String nameWithPackage) {

        boolean exists = false;

        try {
            Class.forName(nameWithPackage, false, this.getClass()
                    .getClassLoader());
            exists = true;
        } catch (ClassNotFoundException e) {
            exists = false;
        }

        return exists;

    }

    protected String resolveApplicationClassName(String classLocationAsDefinedByNinja) {
        if (applicationModulesBasePackage.isPresent()) {
            return new StringBuilder()
                .append(applicationModulesBasePackage.get())
                .append('.')
                .append(classLocationAsDefinedByNinja)
                .toString();
        } else {
            return classLocationAsDefinedByNinja;
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy