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);
}
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;
}
}
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;
}
Aggregations