Search in sources :

Example 1 with GuiceRole

use of com.peterphi.std.guice.apploader.GuiceRole in project stdlib by petergeneric.

the class GuiceFactory method build.

/**
 * Build a guice environment; this is achieved in the following stages:
 * <ol>
 * <li>Load GuiceRole implementations using {@link ServiceLoader} Service Provider Interface</li>
 * <li>Allow all GuiceRole instances to add/remove/change base configuration</li>
 * <li>Load configuration file resources (e.g. environment.properties)</li>
 * <li>Load network configuration (if enabled)</li>
 * <li>Add special GuiceRole for network configuration auto-reload (if network configuration enabled)</li>
 * <li>Load the override configuration file (if present)</li>
 * <li>Set up the classpath scanner (using property {@link GuiceProperties#SCAN_PACKAGES})</li>
 * <li>Instantiate the {@link GuiceSetup} class specified in {@link GuiceProperties#SETUP_PROPERTY}</li>
 * <li>Hand over the GuiceSetup, Roles, Configuration and Classpath Scanner to {@link #createInjector(GuiceRegistry, *
 * ClassScannerFactory, GuiceConfig, GuiceSetup, List)}</li>
 * </ol>
 *
 * @param registry
 * 		(optional) the GuiceRegistry to expose to the Guice environment
 * @param scannerFactory
 * 		(optional) classpath scanner to use
 * @param configs
 * 		base configurations to use
 * @param roles
 * 		base roles to use
 * @param staticSetup
 * 		(optional) a {@link GuiceSetup} implementation to use instead of loading the class name from a property
 * @param autoLoadProperties
 * 		if true, environment.properties etc. and network configuration will be loaded from disk
 * @param autoLoadRoles
 * 		if true, roles will be loaded using the Service Provider Interface
 * @param classloader
 * 		the classloader to use when loading environment.properties etc.
 *
 * @return
 */
static Injector build(final GuiceRegistry registry, ClassScannerFactory scannerFactory, final List<PropertyFile> configs, final List<GuiceRole> roles, final GuiceSetup staticSetup, final boolean autoLoadProperties, final boolean autoLoadRoles, final ClassLoader classloader) {
    final ServiceLoader<GuiceRole> loader = ServiceLoader.load(GuiceRole.class);
    // Find additional guice roles from jar files using the Service Provider Interface
    if (autoLoadRoles) {
        Iterator<GuiceRole> it = loader.iterator();
        while (it.hasNext()) {
            final GuiceRole role = it.next();
            log.debug("Discovered guice role: " + role);
            roles.add(role);
        }
    }
    // Make sure that the first most basic level of properties is the system environment variables
    configs.add(0, getAllEnvironmentVariables());
    // Allow all GuiceRole implementations to add/remove/reorder configuration sources
    for (GuiceRole role : roles) {
        log.debug("Adding requested guice role: " + role);
        role.adjustConfigurations(configs);
    }
    GuiceConfig properties = new GuiceConfig();
    // Generate a random instance ID for this instance of the guice environment
    final String instanceId = SimpleId.alphanumeric(32);
    // Make the randomly generated instance id available to others
    properties.set(GuiceProperties.INSTANCE_ID, instanceId);
    for (PropertyFile config : configs) properties.setAll(config);
    // Load all the core property files?
    if (autoLoadProperties) {
        applyConfigs(classloader, properties);
    }
    // This is a bit of a hack really, but let's insert the GuiceRole for network config if network config is enabled
    if (hasNetworkConfiguration(properties)) {
        final NetworkConfigGuiceRole role = new NetworkConfigGuiceRole();
        roles.add(role);
    }
    // Read the override configuration property to find the override config file
    // Load the override config file and pass that along too.
    PropertyFile overrideFile = load(properties.get(GuiceProperties.OVERRIDE_FILE_PROPERTY));
    // If there are overrides then rebuild the configuration to reflect it
    if (overrideFile != null) {
        log.debug("Applying overrides: " + overrideFile.getFile());
        properties.setOverrides(overrideFile.toMap());
    }
    // Set up the class scanner factory (if the scanner property is set and one has not been provided)
    if (scannerFactory == null) {
        List<String> packages = properties.getList(GuiceProperties.SCAN_PACKAGES, Collections.emptyList());
        if (packages != null && !packages.isEmpty())
            scannerFactory = new ClassScannerFactory(packages.toArray(new String[packages.size()]));
        else
            throw new IllegalArgumentException("Property " + GuiceProperties.SCAN_PACKAGES + " has not been set!");
    }
    final GuiceSetup setup;
    if (staticSetup == null) {
        // Load the Setup property and load the Setup class
        final Class<? extends GuiceSetup> setupClass = getClass(properties, GuiceSetup.class, GuiceProperties.SETUP_PROPERTY);
        try {
            if (setupClass == null)
                throw new IllegalArgumentException("Could not find a setup class!");
            setup = setupClass.newInstance();
            log.debug("Constructed GuiceSetup: " + setupClass);
        } catch (InstantiationException | IllegalAccessException e) {
            throw new IllegalArgumentException("Error constructing instance of " + setupClass, e);
        }
    } else {
        log.debug("Using static GuiceSetup: " + staticSetup);
        setup = staticSetup;
    }
    return createInjector(registry, scannerFactory, properties, setup, roles);
}
Also used : GuiceConfig(com.peterphi.std.guice.common.serviceprops.composite.GuiceConfig) GuiceSetup(com.peterphi.std.guice.apploader.GuiceSetup) GuiceRole(com.peterphi.std.guice.apploader.GuiceRole) NetworkConfigGuiceRole(com.peterphi.std.guice.common.serviceprops.net.NetworkConfigGuiceRole) PropertyFile(com.peterphi.std.io.PropertyFile) ClassScannerFactory(com.peterphi.std.guice.common.ClassScannerFactory) NetworkConfigGuiceRole(com.peterphi.std.guice.common.serviceprops.net.NetworkConfigGuiceRole)

Example 2 with GuiceRole

use of com.peterphi.std.guice.apploader.GuiceRole in project stdlib by petergeneric.

the class GuiceFactory method createInjector.

/**
 * Sets up a Guice Injector; this is achieved in the following stages:
 * <ol>
 * <li>Set up the Shutdown Manager</li>
 * <li>Set up the Metrics Registry</li>
 * <li>Call {@link GuiceRole#register} on all GuiceRoles - this allows modules supporting core plugin functionality to be set
 * up </li>
 * <li>Call {@link GuiceSetup#registerModules} on GuiceSetup to get the application's guice modules - this
 * allows the application to set up helper modules</li>
 * <li>Call {@link GuiceRole#injectorCreated} on all GuiceRoles with the newly-created {@link Injector} - this allows plugins
 * to do one-time
 * post-construction work that requires an Injector</li>
 * <li>Call {@link GuiceSetup#injectorCreated} with the newly-created {@link Injector} - this allows the
 * application
 * to do one-time post-construction work that requires an Injector</li>
 * </ol>
 *
 * @param registry
 * 		(optional) the {@link GuiceRegistry} to expose to the guice environment
 * @param scannerFactory
 * 		the classpath scanner
 * @param config
 * 		the system configuration
 * @param setup
 * 		the setup class
 * @param roles
 * 		guice roles to use
 *
 * @return
 */
private static Injector createInjector(GuiceRegistry registry, ClassScannerFactory scannerFactory, GuiceConfig config, GuiceSetup setup, List<GuiceRole> roles) {
    final long started = System.currentTimeMillis();
    AtomicReference<Injector> injectorRef = new AtomicReference<>();
    List<Module> modules = new ArrayList<>();
    final Stage stage = Stage.valueOf(config.get(GuiceProperties.STAGE_PROPERTY, Stage.DEVELOPMENT.name()));
    // Set up the shutdown module
    ShutdownModule shutdown = new ShutdownModule();
    // If a service manager endpoint is specified (and skip isn't set) then set up the service manager client
    if (config.get("service.service-manager.endpoint") != null && !config.getBoolean(GuiceProperties.SERVICE_MANAGER_SKIP, false)) {
        modules.add(new ServiceManagerClientGuiceModule(config, shutdown.getShutdownManager()));
    } else {
        // Don't store logs in memory waiting for the service manager, they will never be picked up
        ServiceManagerAppender.shutdown();
    }
    final MetricRegistry metricRegistry = CoreMetricsModule.buildRegistry();
    try {
        // Hold a strong reference to the ClassScanner instance to help the JVM not garbage collect it during startup
        // N.B. we don't actually do anything with the scanner in this method (other than read metrics)
        final ClassScanner scanner = scannerFactory.getInstance();
        modules.add(shutdown);
        if (registry != null)
            modules.add(new GuiceRegistryModule(registry));
        // Initialise all the roles
        for (GuiceRole role : roles) role.register(stage, scannerFactory, config, setup, modules, injectorRef, metricRegistry);
        // Initialise the Setup class
        setup.registerModules(modules, config);
        if (log.isTraceEnabled())
            log.trace("Creating Injector with modules: " + modules);
        final Injector injector = Guice.createInjector(stage, modules);
        injectorRef.set(injector);
        for (GuiceRole role : roles) role.injectorCreated(stage, scannerFactory, config, setup, modules, injectorRef, metricRegistry);
        setup.injectorCreated(injector);
        if (scannerFactory != null) {
            final long finished = System.currentTimeMillis();
            final String contextName = config.get(GuiceProperties.SERVLET_CONTEXT_NAME, "(app)");
            log.debug("Injector for " + contextName + " created in " + (finished - started) + " ms");
            if (scanner != null)
                log.debug("Class scanner stats: insts=" + scannerFactory.getMetricNewInstanceCount() + " cached createTime=" + scanner.getConstructionTime() + ", scanTime=" + scanner.getSearchTime());
        }
        return injector;
    } catch (Throwable t) {
        log.error("Error creating injector", t);
        shutdown.shutdown();
        throw t;
    }
}
Also used : MetricRegistry(com.codahale.metrics.MetricRegistry) ArrayList(java.util.ArrayList) AtomicReference(java.util.concurrent.atomic.AtomicReference) ServiceManagerClientGuiceModule(com.peterphi.std.guice.common.logging.ServiceManagerClientGuiceModule) GuiceRole(com.peterphi.std.guice.apploader.GuiceRole) NetworkConfigGuiceRole(com.peterphi.std.guice.common.serviceprops.net.NetworkConfigGuiceRole) ClassScanner(com.peterphi.std.guice.common.ClassScanner) Injector(com.google.inject.Injector) ShutdownModule(com.peterphi.std.guice.common.shutdown.ShutdownModule) Stage(com.google.inject.Stage) Module(com.google.inject.Module) ServiceManagerClientGuiceModule(com.peterphi.std.guice.common.logging.ServiceManagerClientGuiceModule) CoreMetricsModule(com.peterphi.std.guice.common.metrics.CoreMetricsModule) ShutdownModule(com.peterphi.std.guice.common.shutdown.ShutdownModule)

Example 3 with GuiceRole

use of com.peterphi.std.guice.apploader.GuiceRole in project stdlib by petergeneric.

the class GuiceRegistryBuilder method createRegistry.

private static GuiceRegistry createRegistry(GuiceConfig config, TestClass clazz) {
    GuiceRegistry registry = new GuiceRegistry();
    ClassScannerFactory scanner = null;
    GuiceRole[] roles = null;
    if (config != null) {
        if (config.packages().length > 0 || config.classPackages().length > 0) {
            Set<String> packages = new HashSet<>();
            packages.addAll(Arrays.asList(config.packages()));
            for (Class c : config.classPackages()) packages.add(c.getPackage().getName());
            scanner = new ClassScannerFactory(packages.toArray(new String[packages.size()]));
        }
        if (config.role().length > 0) {
            List<GuiceRole> instances = new ArrayList<>();
            for (Class<? extends GuiceRole> role : config.role()) {
                try {
                    instances.add(role.newInstance());
                } catch (Exception e) {
                    throw new IllegalArgumentException("Error instantiating GuiceRole " + role, e);
                }
            }
            roles = instances.toArray(new GuiceRole[instances.size()]);
        }
    }
    GuiceBuilder builder = registry.getBuilder();
    if (scanner != null)
        builder.withScannerFactory(scanner);
    else
        builder.withNoScannerFactory();
    if (config != null && config.config().length > 0)
        builder.withConfig(config.config());
    if (config != null)
        builder.withAutoLoadRoles(config.autoLoadRoles());
    if (roles != null)
        builder.withRole(roles);
    // Add local method config sources
    {
        validateGuiceTestConfigMethods(clazz);
        for (Object src : clazz.getAnnotatedMethodValues(null, TestConfig.class, Object.class)) {
            if (src instanceof Properties)
                builder.withConfig((Properties) src);
            else if (src instanceof PropertyFile)
                builder.withConfig((PropertyFile) src);
        }
    }
    // Add local method module sources
    {
        validateGuiceTestModuleMethods(clazz);
        builder.withRole(new ModuleAddingGuiceRole(clazz.getAnnotatedMethodValues(null, TestModule.class, Module.class)));
    }
    // Auto-detect @Automock annotated fields in the test and create mocks for them
    {
        List<FrameworkField> fields = clazz.getAnnotatedFields(Automock.class);
        if (fields.size() > 0)
            builder.withRole(new ModuleAddingGuiceRole(new AutomockAnnotatedMockModule(clazz.getJavaClass(), fields)));
    }
    // Make sure we set the unit test property so roles are aware they're running in a unit test (e.g. so they don't auto-register REST services)
    {
        PropertyFile props = new PropertyFile();
        props.set(GuiceProperties.UNIT_TEST, "true");
        builder.withConfig(props);
    }
    // Add the Setup class, or if none is specified then add local modules:
    if (config != null && config.setup().length > 0) {
        builder.withSetup(config.setup()[0]);
    } else {
        builder.withSetup(new BasicSetup());
    }
    return registry;
}
Also used : ArrayList(java.util.ArrayList) Properties(java.util.Properties) GuiceProperties(com.peterphi.std.guice.apploader.GuiceProperties) ArrayList(java.util.ArrayList) List(java.util.List) ClassScannerFactory(com.peterphi.std.guice.common.ClassScannerFactory) HashSet(java.util.HashSet) TestConfig(com.peterphi.std.guice.testing.com.peterphi.std.guice.testing.annotations.TestConfig) GuiceBuilder(com.peterphi.std.guice.apploader.impl.GuiceBuilder) GuiceRegistry(com.peterphi.std.guice.apploader.impl.GuiceRegistry) GuiceRole(com.peterphi.std.guice.apploader.GuiceRole) PropertyFile(com.peterphi.std.io.PropertyFile) BasicSetup(com.peterphi.std.guice.apploader.BasicSetup) TestClass(org.junit.runners.model.TestClass) Module(com.google.inject.Module) TestModule(com.peterphi.std.guice.testing.com.peterphi.std.guice.testing.annotations.TestModule) TestModule(com.peterphi.std.guice.testing.com.peterphi.std.guice.testing.annotations.TestModule) Automock(com.peterphi.std.guice.testing.com.peterphi.std.guice.testing.annotations.Automock)

Aggregations

GuiceRole (com.peterphi.std.guice.apploader.GuiceRole)3 Module (com.google.inject.Module)2 ClassScannerFactory (com.peterphi.std.guice.common.ClassScannerFactory)2 NetworkConfigGuiceRole (com.peterphi.std.guice.common.serviceprops.net.NetworkConfigGuiceRole)2 PropertyFile (com.peterphi.std.io.PropertyFile)2 ArrayList (java.util.ArrayList)2 MetricRegistry (com.codahale.metrics.MetricRegistry)1 Injector (com.google.inject.Injector)1 Stage (com.google.inject.Stage)1 BasicSetup (com.peterphi.std.guice.apploader.BasicSetup)1 GuiceProperties (com.peterphi.std.guice.apploader.GuiceProperties)1 GuiceSetup (com.peterphi.std.guice.apploader.GuiceSetup)1 GuiceBuilder (com.peterphi.std.guice.apploader.impl.GuiceBuilder)1 GuiceRegistry (com.peterphi.std.guice.apploader.impl.GuiceRegistry)1 ClassScanner (com.peterphi.std.guice.common.ClassScanner)1 ServiceManagerClientGuiceModule (com.peterphi.std.guice.common.logging.ServiceManagerClientGuiceModule)1 CoreMetricsModule (com.peterphi.std.guice.common.metrics.CoreMetricsModule)1 GuiceConfig (com.peterphi.std.guice.common.serviceprops.composite.GuiceConfig)1 ShutdownModule (com.peterphi.std.guice.common.shutdown.ShutdownModule)1 Automock (com.peterphi.std.guice.testing.com.peterphi.std.guice.testing.annotations.Automock)1