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

com.avanza.astrix.context.AstrixConfigurer Maven / Gradle / Ivy

/*
 * Copyright 2014 Avanza Bank AB
 *
 * 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 com.avanza.astrix.context;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.stream.Stream;

import com.avanza.astrix.beans.api.ApiProviderBeanPublisherModule;
import com.avanza.astrix.beans.config.AstrixConfig;
import com.avanza.astrix.beans.config.AstrixConfigModule;
import com.avanza.astrix.beans.configdiscovery.ConfigDiscoveryModule;
import com.avanza.astrix.beans.core.AstrixBeanKey;
import com.avanza.astrix.beans.core.AstrixBeanSettings.BeanSetting;
import com.avanza.astrix.beans.core.AstrixBeanSettings.BooleanBeanSetting;
import com.avanza.astrix.beans.core.AstrixBeanSettings.IntBeanSetting;
import com.avanza.astrix.beans.core.AstrixBeanSettings.LongBeanSetting;
import com.avanza.astrix.beans.core.AstrixBeansCoreModule;
import com.avanza.astrix.beans.core.AstrixConfigAware;
import com.avanza.astrix.beans.core.AstrixSettings;
import com.avanza.astrix.beans.factory.BeanFactoryModule;
import com.avanza.astrix.beans.factory.StandardFactoryBean;
import com.avanza.astrix.beans.ft.*;
import com.avanza.astrix.beans.publish.*;
import com.avanza.astrix.beans.registry.AstrixServiceRegistryLibraryProvider;
import com.avanza.astrix.beans.registry.AstrixServiceRegistryServiceProvider;
import com.avanza.astrix.beans.registry.ServiceRegistryDiscoveryModule;
import com.avanza.astrix.beans.service.DirectComponentModule;
import com.avanza.astrix.beans.service.ServiceModule;
import com.avanza.astrix.config.*;
import com.avanza.astrix.context.mbeans.AstrixMBeanModule;
import com.avanza.astrix.context.mbeans.MBeanServerFacade;
import com.avanza.astrix.context.mbeans.PlatformMBeanServer;
import com.avanza.astrix.context.metrics.DefaultMetricSpi;
import com.avanza.astrix.context.metrics.MetricsModule;
import com.avanza.astrix.context.metrics.MetricsSpi;
import com.avanza.astrix.modules.*;
import com.avanza.astrix.provider.core.AstrixApiProvider;
import com.avanza.astrix.provider.core.AstrixExcludedByProfile;
import com.avanza.astrix.provider.core.AstrixIncludedByProfile;
import com.avanza.astrix.serviceunit.AstrixApplicationDescriptor;
import com.avanza.astrix.serviceunit.ServiceUnitModule;
import com.avanza.astrix.serviceunit.SystemServiceApiProvider;
import com.avanza.astrix.versioning.core.ObjectSerializerModule;
import com.avanza.astrix.versioning.jackson2.Jackson2SerializerModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * Used to configure and create an {@link AstrixContext}. 

* * @author Elias Lindholm (elilin) * */ public class AstrixConfigurer { private static final String CLASSPATH_OVERRIDE_SETTINGS = "META-INF/astrix/settings.properties"; private static final Logger log = LoggerFactory.getLogger(AstrixConfigurer.class); private ApiProviders astrixApiProviders; private final Collection> standaloneFactories = new LinkedList<>(); private final List customModules = new ArrayList<>(); private final Map, StrategyProvider> strategyProviderByType = new HashMap<>(); private final MapConfigSource settings = new MapConfigSource(); private DynamicConfig customConfig = null; private final DynamicConfig wellKnownConfigSources = DynamicConfig.create(new SystemPropertiesConfigSource(), settings, PropertiesConfigSource.optionalClasspathPropertiesFile(CLASSPATH_OVERRIDE_SETTINGS)); private final Set activeProfiles = new HashSet<>(); private AstrixApplicationDescriptor applicationDescriptor; public AstrixConfigurer() { } public void setApplicationDescriptor(AstrixApplicationDescriptor applicationDescriptor) { this.applicationDescriptor = applicationDescriptor; } /** * Creates an AstrixContext instance using the current configuration.

* * @return */ public AstrixContext configure() { DynamicConfig config = createDynamicConfig(); ModulesConfigurer modulesConfigurer = new ModulesConfigurer(); modulesConfigurer.registerDefault(StrategyProvider.create(HystrixCommandNamingStrategy.class, DefaultHystrixCommandNamingStrategy.class)); modulesConfigurer.registerDefault(StrategyProvider.create(BeanFaultToleranceFactorySpi.class, NoFaultTolerance.class)); modulesConfigurer.registerDefault(StrategyProvider.create(MetricsSpi.class, DefaultMetricSpi.class)); modulesConfigurer.registerDefault(StrategyProvider.create(MBeanServerFacade.class, PlatformMBeanServer.class, context -> context.importType(AstrixConfig.class))); for (Module plugin : customModules) { modulesConfigurer.register(plugin); } loadAstrixContextPlugins(modulesConfigurer); for (StrategyProvider strategyProvider : this.strategyProviderByType.values()) { modulesConfigurer.register(strategyProvider); } modulesConfigurer.register(new AstrixConfigModule(config, this.settings)); modulesConfigurer.register(new DirectComponentModule()); modulesConfigurer.register(new AstrixBeansCoreModule()); modulesConfigurer.register(new MetricsModule()); modulesConfigurer.register(new AstrixMBeanModule()); modulesConfigurer.register(new ServiceRegistryDiscoveryModule()); modulesConfigurer.register(new ConfigDiscoveryModule()); modulesConfigurer.register(new BeansPublishModule()); modulesConfigurer.register(new ServiceModule()); modulesConfigurer.register(new ObjectSerializerModule()); modulesConfigurer.register(new Jackson2SerializerModule()); modulesConfigurer.register(new ApiProviderBeanPublisherModule()); modulesConfigurer.register(new FaultToleranceModule()); modulesConfigurer.register(new BeanFactoryModule()); if (this.applicationDescriptor != null) { // Init server parts setupApplicationInstanceId(config); modulesConfigurer.register(new ServiceUnitModule(this.applicationDescriptor)); } modulesConfigurer.registerBeanPostProcessor(new AstrixAwareInjector(config)); Modules modules = modulesConfigurer.configure(); final AstrixContextImpl context = new AstrixContextImpl(modules, this.applicationDescriptor); Stream systemApis = Stream.of(AstrixServiceRegistryServiceProvider.class, AstrixServiceRegistryLibraryProvider.class, SystemServiceApiProvider.class) .map(ApiProviderClass::create); Stream.concat(systemApis, getApiProviders(modules, config)) .filter(this::isActive) .distinct() .forEach(context::register); // TODO: Merge with FilteredApiProviders and create module for (StandardFactoryBean beanFactory : standaloneFactories) { log.debug("Registering standalone factory: bean={}", beanFactory.getBeanKey()); context.registerBeanFactory(beanFactory); } return context; } private void setupApplicationInstanceId(DynamicConfig config) { String applicationInstanceId = AstrixSettings.APPLICATION_INSTANCE_ID.getFrom(config).get(); if (applicationInstanceId == null) { applicationInstanceId = this.applicationDescriptor.toString(); set(AstrixSettings.APPLICATION_INSTANCE_ID, this.applicationDescriptor.toString()); log.info("No applicationInstanceId set, using name of ApplicationDescriptor as applicationInstanceId: {}", applicationInstanceId); Objects.requireNonNull(AstrixSettings.APPLICATION_INSTANCE_ID.getFrom(config).get()); } else { log.info("Current applicationInstanceId={}", applicationInstanceId); } } private void loadAstrixContextPlugins(final ModulesConfigurer modulesConfigurer) { Iterator contextPlugins = ServiceLoader.load(AstrixContextPlugin.class).iterator(); while (contextPlugins.hasNext()) { AstrixContextPlugin contextPlugin = contextPlugins.next(); log.debug("Registering AstrixContextPlugin: astrixContextPlugin={}", contextPlugin.getClass().getName()); contextPlugin.registerStrategies(new AstrixStrategiesConfig() { @Override public void registerDefaultStrategy(Class strategyType, Class strategyProvider) { modulesConfigurer.registerDefault(StrategyProvider.create(strategyType, strategyProvider)); } @Override public void registerStrategy(Class strategyType, Class strategyImpl) { modulesConfigurer.register(StrategyProvider.create(strategyType, strategyImpl)); } @Override public void registerStrategy(Class strategyType, Class strategyImpl, StrategyContextPreparer contextPreparer) { modulesConfigurer.register(StrategyProvider.create(strategyType, strategyImpl, contextPreparer)); } }); modulesConfigurer.register(contextPlugin); } } private DynamicConfig createDynamicConfig() { if (customConfig != null) { return DynamicConfig.merged(customConfig, wellKnownConfigSources); } String dynamicConfigFactoryClass = AstrixSettings.DYNAMIC_CONFIG_FACTORY.getFrom(wellKnownConfigSources).get(); if (dynamicConfigFactoryClass != null) { AstrixDynamicConfigFactory dynamicConfigFactory = initFactory(dynamicConfigFactoryClass); DynamicConfig config = dynamicConfigFactory.create(); return DynamicConfig.merged(config, wellKnownConfigSources); } return wellKnownConfigSources; } private AstrixDynamicConfigFactory initFactory(String dynamicConfigFactoryClass) { try { return (AstrixDynamicConfigFactory) Class.forName(dynamicConfigFactoryClass).newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { throw new RuntimeException("Failed to init AstrixDynamicConfigFactoryClass: " + dynamicConfigFactoryClass, e); } } /* * Allows api's published using astrix (using @AstrixApiProvider) to have the * DynamicConfig instance associated with the current AstrixContext injected */ private final class AstrixAwareInjector implements ModuleInstancePostProcessor { private final DynamicConfig config; public AstrixAwareInjector(DynamicConfig config) { this.config = config; } @Override public void postProcess(Object bean) { if (bean instanceof AstrixConfigAware) { AstrixConfigAware.class.cast(bean).setConfig(config); // TODO: config } } } private boolean isActive(ApiProviderClass providerClass) { if (providerClass.isAnnotationPresent(AstrixIncludedByProfile.class)) { AstrixIncludedByProfile activatedBy = providerClass.getAnnotation(AstrixIncludedByProfile.class); if (!this.activeProfiles.contains(activatedBy.value())) { log.debug("Rejecting provider, required profile not active. profile={} provider={}", activatedBy.value(), providerClass.getProviderClassName()); return false; } } if (providerClass.isAnnotationPresent(AstrixExcludedByProfile.class)) { AstrixExcludedByProfile deactivatedBy = providerClass.getAnnotation(AstrixExcludedByProfile.class); if (this.activeProfiles.contains(deactivatedBy.value())) { log.debug("Rejecting provider, excluded by active profile. profile={} provider={}", deactivatedBy.value(), providerClass.getProviderClassName()); return false; } } log.debug("Found provider: provider={}", providerClass.getProviderClassName()); return true; } private Stream getApiProviders(Modules modules, DynamicConfig config) { if (this.astrixApiProviders != null) { return astrixApiProviders.getAll(); } String basePackage = AstrixSettings.API_PROVIDER_SCANNER_BASE_PACKAGE.getFrom(config).get(); if (!basePackage.trim().isEmpty()) { return new AstrixApiProviderClassScanner(getAllApiProviderAnnotationsTypes(modules), "com.avanza.astrix", basePackage.split(",")).getAll(); // Always scan com.avanza.astrix package } return new AstrixApiProviderClassScanner(getAllApiProviderAnnotationsTypes(modules), "com.avanza.astrix").getAll(); } private List> getAllApiProviderAnnotationsTypes(Modules modules) { List> result = new ArrayList<>(); for (BeanPublisherPlugin plugin : modules.getInstance(ApiProviderPlugins.class).getAll()) { result.add(plugin.getProviderAnnotationType()); } return result; } /** * Sets the base-package used when scanning for {@link AstrixApiProvider}'s.

* * @param basePackage * @return */ public AstrixConfigurer setBasePackage(String basePackage) { this.settings.set(AstrixSettings.API_PROVIDER_SCANNER_BASE_PACKAGE, basePackage); return this; } public AstrixConfigurer enableFaultTolerance(boolean enableFaultTolerance) { this.settings.set(AstrixSettings.ENABLE_FAULT_TOLERANCE, enableFaultTolerance); return this; } // package private. Used for internal testing only void setAstrixApiProviders(ApiProviders astrixApiProviders) { this.astrixApiProviders = astrixApiProviders; } // package private. Used for internal testing only AstrixConfigurer registerPlugin(final Class type, final T provider) { customModules.add(new Module() { @Override public void prepare(ModuleContext pluginContext) { pluginContext.bind(type, provider); pluginContext.export(type); } @Override public String name() { return "plugin-" + type.getName() + "[" + provider.getClass().getName() + "]"; } }); return this; } // package private. Used for internal testing only void registerStrategy(final Class strategyInterface, final T strategyInstance) { this.strategyProviderByType.put(strategyInterface, StrategyProvider.create(strategyInterface, strategyInstance)); } public AstrixConfigurer set(String settingName, long value) { this.settings.set(settingName, Long.toString(value)); return this; } public AstrixConfigurer set(String settingName, boolean value) { this.settings.set(settingName, Boolean.toString(value)); return this; } public AstrixConfigurer set(String settingName, String value) { this.settings.set(settingName, value); return this; } public final AstrixConfigurer set(Setting setting, T value) { this.settings.set(setting, value); return this; } public final AstrixConfigurer set(LongSetting setting, long value) { this.settings.set(setting, value); return this; } public AstrixConfigurer setSettings(Map settings) { for (Map.Entry setting : settings.entrySet()) { this.settings.set(setting.getKey(), setting.getValue()); } return this; } /** * Sets the custom configuration sources that should be used by the AstrixContext. * * When set, then the given DynamicConfig instance will take precedence over all well-known configuration * sources, see list below. When this property is set, Astrix will NOT look for a {@link AstrixDynamicConfigFactory} * to create the custom configuration. If NOT set, then astrix will query all well-known configuration sources * for a AstrixDynamicConfigFactory. It one is found, than that factory will be used to create a DynamicConfig instance * for the custom configuration sources, otherwise no custom configuration sources will be used by the * created AstrixContext (that is, only well-known configuration sources will be used). * *

List of well-known configuration sources
*
    *
  1. System Properties
  2. *
  3. Programmatic configuration set on this instance
  4. *
  5. META-INF/astrix/settings.properties
  6. *
  7. default values
  8. *
* * @param config custom DynamicConfig to use * @return */ public AstrixConfigurer setConfig(DynamicConfig config) { this.customConfig = config; return this; } /** * Optional property that identifies what subsystem the current context belongs to. Its only * allowed to invoke non-versioned services within the same subsystem. Attempting * to invoke a non-versioned service in another subsystem will throw an IllegalSubsystemException.

* * @param string * @return */ public AstrixConfigurer setSubsystem(String subsystem) { this.settings.set(AstrixSettings.SUBSYSTEM_NAME, subsystem); return this; } void addFactoryBean(StandardFactoryBean factoryBean) { this.standaloneFactories.add(factoryBean); } void removeSetting(String name) { this.settings.set(name, null); } /** * Activates a given Astrix profile. * * Astrix profiles are used to include/exclude {@link AstrixApiProvider}'s at runtime by annotating them * with {@link AstrixIncludedByProfile} and/or {@link AstrixExcludedByProfile}, typically to * replace a given {@link AstrixApiProvider} in testing scenarios.

* * * @param profile * @return */ public AstrixConfigurer activateProfile(String profile) { this.activeProfiles.add(profile); return this; } public void set(BooleanBeanSetting beanSetting, AstrixBeanKey beanKey, boolean value) { set(beanSetting.nameFor(beanKey), value); } public void set(IntBeanSetting beanSetting, AstrixBeanKey beanKey, int value) { set(beanSetting.nameFor(beanKey), value); } public void set(LongBeanSetting beanSetting, AstrixBeanKey beanKey, long value) { set(beanSetting.nameFor(beanKey), value); } void registerModule(Module module) { this.customModules.add(module); } public void set(BeanSetting setting, AstrixBeanKey beanKey, T value) { set(setting.nameFor(beanKey), asString(value)); } private String asString(Object value) { if (value == null) { return null; } return value.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy