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

com.avanza.astrix.spring.AstrixFrameworkBean 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.spring;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PreDestroy;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.core.Ordered;

import com.avanza.astrix.beans.core.AstrixBeanSettings.BeanSetting;
import com.avanza.astrix.beans.core.AstrixBeanKey;
import com.avanza.astrix.beans.core.AstrixSettings;
import com.avanza.astrix.config.DynamicConfig;
import com.avanza.astrix.config.Setting;
import com.avanza.astrix.context.Astrix;
import com.avanza.astrix.context.AstrixApplicationContext;
import com.avanza.astrix.context.AstrixConfigurer;
import com.avanza.astrix.context.AstrixContext;
import com.avanza.astrix.serviceunit.AstrixApplicationDescriptor;
import com.avanza.astrix.serviceunit.ServiceExporter;

/**
 * 
 * @author Elias Lindholm (elilin)
 */
public class AstrixFrameworkBean implements BeanFactoryPostProcessor, ApplicationContextAware, ApplicationListener, Ordered {
	
	/*
	 * A NOTE ON THE IMPLEMENTATION NOTE: This comment is outdated...
	 * 
	 * IMPLEMENTATION NOTE - Astrix startup
	 * 
	 * The startup procedure goes as follows:
	 * 
	 * 1. BeanFactoryPostProcessor (BFPP)
	 *  - The BFPP will register an instance for each consumedAstrixBean in the BeanFactory
	 *  - The BFPP will also register an instance of AstrixSpingContext to act as bridge
	 *    between spring and astrix.
	 *    
	 * 2. BeanPostProcessor (BPP)
	 *  - The BPP will investigate each spring-bean in the current application and search
	 *    for @AstrixServiceExport annotated classes and register those with the ServiceRegistryExporterClient
	 *    
	 * 3. ApplicationListener
	 *  - After each spring-bean have bean created and fully initialized this class will receive a call-back
	 *    and start exporting services using the ServiceRegistryExporterClient.
	 *    
	 * 
	 * Note that this class (AstrixFrameworkBean) is the only class in the framework that will recieve spring-lifecylce events.
	 * 
	 */
	
	private List> consumedAstrixBeans = new ArrayList<>();
	private String subsystem;
	private Map settings = new HashMap<>();
	private AstrixApplicationDescriptor applicationDescriptor;
	private AstrixApplicationContext astrixContext;
	private volatile boolean servicePublisherStarted = false;
	private ApplicationContext applicationContext;
	private final AstrixConfigurer configurer = new AstrixConfigurer();
	
	public AstrixFrameworkBean() {
	}
	
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		astrixContext = createAstrixContext(getDynamicConfig(applicationContext));
		astrixContext.getInstance(AstrixSpringContext.class).setApplicationContext(applicationContext);
		astrixContext.getInstance(AstrixSpringContext.class).setAstrixContext(astrixContext);
		for (Class consumedAstrixBean : this.consumedAstrixBeans) {
			beanFactory.registerSingleton(consumedAstrixBean.getName(), astrixContext.getBean(consumedAstrixBean));
		}
		beanFactory.registerSingleton(AstrixSpringContext.class.getName(), astrixContext.getInstance(AstrixSpringContext.class));
		beanFactory.registerSingleton(AstrixContext.class.getName(), astrixContext);
		if (isServer()) {
			beanFactory.addBeanPostProcessor(new AstrixBeanPostProcessor(astrixContext.getInstance(ServiceExporter.class)));
		}
	}

	/**
	 * All consumedAstrixBeans will be created (see {@link Astrix#getBean(Class)} and registered in the spring ApplicationContext at
	 * startup. All consumedAstrixBeans will be available as autowiring candidates for other beans in the current spring ApplicationContext.

* * Its also possible to wire consumedAstrixBeans by reference. Each consumed Astrix-bean will be registered under the fully qualified * class name of the given Astrix bean.

* *

	 * Example:
	 * 
	 * setConsumedAstrixBeans(asList(se.avanza.customer.CustomerService.class));
	 * 
	 * Creates and registers an Astrix bean of type CustomerService in the current ApplicationContext. The name
	 * of the CustomerService bean in the spring ApplicationContext will be "se.avanza.customer.CustomerService", which
	 * can be used in a ApplicationContext-xml file to explicitly wire an instance of CustomerService into another
	 * spring bean.
	 * 
* * @param consumedAstrixBeans */ public void setConsumedAstrixBeans(List> consumedAstrixBeans) { this.consumedAstrixBeans = consumedAstrixBeans; } public void setSettings(Map settings) { this.settings.putAll(settings); } public void set(Setting setting, T value) { this.configurer.set(setting, value); } public void set(BeanSetting setting, AstrixBeanKey beanKey, T value) { this.configurer.set(setting, beanKey, value); } public Map getSettings() { return settings; } @PreDestroy public void destroy() { this.astrixContext.destroy(); } /** * If a service descriptor is provided, then the service exporting part of the framework * will be loaded with all required components for the given serviceDescriptor. * * @param serviceDescriptor * @deprecated - replaced by {@link AstrixFrameworkBean#setApplicationDescriptor(Class)} */ @Deprecated public void setServiceDescriptor(Class serviceDescriptorHolder) { this.applicationDescriptor = AstrixApplicationDescriptor.create(serviceDescriptorHolder); } /** * If an application descriptor is set, then the service exporting part of the framework * will be loaded with all required components to provide the services defined in * the api's referred to by the given applicatinDescriptor. * * @param applicationDescriptorHolder */ public void setApplicationDescriptor(Class applicationDescriptorHolder) { this.applicationDescriptor = AstrixApplicationDescriptor.create(applicationDescriptorHolder); } /** * All services consumed by the current application. Each type will be created and available * for autowiring in the current applicationContext. * * Implementation note: This is only application defined usages of Astrix beans. Any Astrix-beans * used internally by the service-framework will not be included in this set. * * @return */ public List> getConsumedAstrixBeans() { return consumedAstrixBeans; } public void setSubsystem(String subsystem) { this.subsystem = subsystem; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } private DynamicConfig getDynamicConfig(ApplicationContext applicationContext) { Collection dynamicConfigs = applicationContext.getBeansOfType(DynamicConfig.class).values(); if (dynamicConfigs.isEmpty()) { return null; } if (dynamicConfigs.size() == 1) { return dynamicConfigs.iterator().next(); } throw new IllegalArgumentException("Multiple DynamicConfig instances found in ApplicationContext"); } private AstrixApplicationContext createAstrixContext(DynamicConfig optionalConfig) { configurer.setSettings(this.settings); if (optionalConfig != null) { configurer.setConfig(optionalConfig); } if (this.subsystem != null) { configurer.setSubsystem(this.subsystem); } if (this.applicationDescriptor != null) { configurer.setApplicationDescriptor(applicationDescriptor); } return (AstrixApplicationContext) configurer.configure(); } @Override public void onApplicationEvent(ApplicationContextEvent event) { if (event instanceof ContextRefreshedEvent && !servicePublisherStarted) { // Application initialization complete. Export astrix-services. if (isServer()) { this.astrixContext.startServicePublisher(); } servicePublisherStarted = true; } else if (event instanceof ContextClosedEvent || event instanceof ContextStoppedEvent) { /* * What's the difference between the "stopped" and "closed" event? In our embedded * integration tests we only receive ContextClosedEvent */ destroyAstrixContext(); } } private void destroyAstrixContext() { this.astrixContext.destroy(); } private boolean isServer() { return applicationDescriptor != null; } public void setConsumedAstrixBeans(Class... consumedAstrixBeans) { setConsumedAstrixBeans(Arrays.asList(consumedAstrixBeans)); } @Override public int getOrder() { // Run this class as late as possible return Ordered.LOWEST_PRECEDENCE; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy