I'm trying to write a multi-tenant Spring Boot application but having trouble to eager initialize beans when the server starts (i.e. not lazily once the tenant requests the bean)
To support multi-tenancy, i created a #CustomerScoped annotation that creates objects based on a ThreadLocal String value.
My configuration provides a bean like this and lazily initializes it:
#Autowired
private AutowireCapableBeanFactory beanFactory;
#Bean
#CustomerScoped
public Scheduler getScheduler() {
CreateDefaults job = factory.createBean(CreateDefaults.class));
Scheduler scheduler = new Scheduler();
scheduler.schedule(job);
return scheduler;
}
#PostConstruct
public void init() {
CustomerScope.setCustomer("tenant1");
getScheduler();
CustomerScope.setCustomer("tenant2");
getScheduler();
CustomerScope.clearCustomer();
}
When starting the server, two Schedulers should be created, each of which would execute their own instance of "Create Defaults".
When tenants access the application themselves, they should be getting their own instance of this Scheduler.
This seems to work but i wonder whether this is the correct way of doing things.
In particular, i am worried about the fact that the beanFactory isn't scoped itself.
Would this approach work and scale for more complex systems?
My code sample was actually correct.
The Beanfactory doesn't need to be scoped itself, it just has to be made aware of the scope, which in my case can be achieved in the configuration:
#Bean
public static CustomScopeConfigurer customScope() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope(CustomerScope.CUSTOMER_SCOPE_NAME, new CustomerScope());
return configurer;
}
Related
I need to inject list of already initialized beans into some another one.
I have class with definitions of some lazy beans which are used depending on environment - like on server 1 only impl1 and impl2 will be used and on server 2 impl3 and impl1
#Component
class Definitions {
#Bean
#Lazy
public A impl1() { /* ... */ }
#Bean
#Lazy
public A impl2() { /* ... */ }
#Bean
#Lazy
public A impl3() { /* ... */ }
}
And I have some monitoring bean which don't know anything about environment and just collects all those A beans exposing some health information for actuator:
#Component
class Monitoring implements HealthIndicator {
#Autowired
private List<A> monitored;
}
Problem is that spring wires all beans into monitored even if they were not initialized before (which crashes the whole thing, cause there is no suitable environment). And I need to somehow explain to spring that I only need already initialized beans - something like #AutowireOnlyThoseLazyBeansWhichAlreadyBeenUsedSomewhereElse
P.S. I know that I can use dirty hack and declare a list property inside Definitions, fill it in bean factory methods and register another one bean with reference to that list but it is too dirty.
Solution was to just write custom OSGi-like "ServiceTracker" through monitoring beans of some specific type via BeanPostProcessor and registering dynamic bean with all tracked beans in concurrent map into context.
I am creating a timer application that uses Quartz, also am using spring to initialise my DB from schema.sql file. When application starts I want to DB be initialised before my Scheduler bean is created.
#Bean
public Scheduler scheduler() throws SchedulerException {
Scheduler scheduler;
final StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory("application.properties");
stdSchedulerFactory.initialize();
scheduler = stdSchedulerFactory.getScheduler();
scheduler.start();
return scheduler;
}
Scheduler bean is inside TimerConfiguration.java which is added to TimerApplication like
#SpringBootApplication #Import({TimerConfiguration.class})
public class TimerApplication {
Is there a way to achieve this ?
The #DependsOn annotation specifies a bean which should be initialized after another bean got initialized.
It's recommended to set the name of the waiting bean as value in the annotation.
In your case its #DependsOn("datasource").
More infos from the docs:
Beans on which the current bean depends. Any beans specified are
guaranteed to be created by the container before this bean. Used
infrequently in cases where a bean does not explicitly depend on
another through properties or constructor arguments, but rather
depends on the side effects of another bean's initialization.
May be used on any class directly or indirectly annotated with Component or
on methods annotated with Bean.
In my Spring/Grails/Groovy app, I configure some cache beans:
rulesCache(InMemoryCache){..}
countriesCache(InMemoryCache){..}
myService(ServiceBean){
cache = ref('rulesCache')
}
A cache manager provides specialized services when retrieving caches, so I give the manager a list of cache beans:
cacheMgr(CacheManager){
caches = [ ref('rulesCache'), ref('countriesCache')]
}
Services must get cache beans from the manager, they can't be "wired in" (the manager returns a cache delegate, not the cache itself, that's why), so I got around this problem by doing:
cacheMgr(CacheManager){
caches = [ ref('rulesCache'), ref('countriesCache')]
}
cacheMgrDelegate(MethodInvokingFactoryBean) { bean ->
bean.dependsOn = ['cacheMgr']
targetObject = cacheMgr
targetMethod = 'getManager'
}
myService(SomeService){
cache = "#{cacheMgrDelegate.getCache('rulesCache')}"
}
This works fine, but cache beans are arbitrary, so I can't provide a list to the manager. I managed to get around this problem by listening for post initialization events from cache type objects, and registering each cache manually with the manager:
CacheManager implements BeanPostProcessor {
postProcessAfterInitialization(bean, beanName){
if(bean instanceof ICache)
registerCache(bean)
return bean
}
}
Problem
The issue is that Spring is doing initialization on myService before cacheManager registers all cache beans, so getCache() returns null:
myService(SomeService){
cache = "#{cacheMgrDelegate.getCache('rulesCache')}"
}
I understand why it's happening. I can't use dependsOn since cache beans are arbitrary, and this is where I'm stuck.
Possible Solution
During spring config phase, CacheManager.getCache(name) could return a lightweight "proxy"-like object while saving a reference to each proxy generated:
getCache(String name){
CacheProxy proxy = createProxy()
proxies.add(proxy)
return proxy
}
After all beans are configured and app context is set, cacheManager simply iterates the list of proxies and completes initialization:
onApplicationContext(){
proxies.each{ proxy ->
completeInit(proxy)
}
}
Is there a better option? I'm out of ideas :-)
Can't you simply autowire all instances of ICache instead? It should create necessary dependencies between CacheManager and the caches:
CacheManager {
#Autowired
public void setCaches(List<ICache> caches) {
...
}
...
}
I have a prototype storm app that reads a STOMP stream and stores the output on HBase. It works, but is not very flexible and I'm trying to get it set up in a more consistent way with the rest of our apps, but not having much luck figuring out how the current way of working with Storm. We use spring-jms classes, but instead of using them in the standard spring way, they are being created at run time, and setting dependencies manually.
This project: https://github.com/granthenke/storm-spring looked promising, but it hasn't been touched in a couple years and doesn't build properly since the storm jars have been taken into apache incubator and repackaged.
Is there something I'm missing, or is it not worth my while to get these things integrated?
#zenbeni has answered this question but I wanna tell you about my implementation, it's hard to make spouts/bolts as spring beans. But to use other spring spring beans inside your spouts/bolts you could declare a global variable & in your execute method check whtether variable is null or not. If it's null you have to get bean from application context. Create a class which contains a method to initialize beans if it's not initialized already. Look ApplicationContextAware interface for more information(Spring bean reuse).
Example Code:
Bolt Class:
public class Class1 implements IRichBolt{
Class2 class2Object;
public void prepare() {
if (class2Object== null) {
class2Object= (Class2) Util
.initializeContext("class2");
}
}
}
Util Class for initializing Beans if not initialized already:
public class Util{
public static Object initializeContext(String beanName) {
Object bean = null;
try {
synchronized (Util.class) {
if (ApplicationContextUtil.getAppContext() == null) {
ApplicationContext appContext = new ClassPathXmlApplicationContext("beans.xml");
bean = ApplicationContextUtil.getAppContext().getBean(
beanName);
} else {
bean = ApplicationContextUtil.getAppContext().getBean(
beanName);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return bean;
}
}
Listener for Application Context Change:
#Component
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext appContext;
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
appContext = applicationContext;
}
public static ApplicationContext getAppContext() {
return appContext;
}
}
Note: Each Worker will initialize spring context because it's running in different JVM.
UPDATE
If you want to use a spring bean class in which you have some values previously assigned try this,
Note: Passing the current class to Bolt's constructor
Class (Topology Creation Class) which is already contains values:
public class StormTopologyClass implements ITopologyBuilder, Serializable {
public Map<String, String> attributes = new HashMap<String, String>();
TopologyBuilder builder=new TopologyBuilder();
builder.setBolt("Class1",new Class1(this));
builder.createTopology();
}
Bolt by using single argument constructor:
public class Class1 implements IRichBolt{
StormTopologyClass topology;
public Class1 (StormTopologyClass topology) {
this.topology = topology;
}
}
Now you could use attributes variable & it's values in bolt class.
In fact, storm-spring seems to be what you are looking for but it is not updated and have limitations (cannot define tasks on bolts / spouts for instance, etc). Maybe you should roll your own integration?
Don't forget your target: a cluster with many workers. How does spring behave when you will deploy your topology with storm api (rebalance for instance) on one more worker? Does it mean it has to instanciate a new Spring context on the worker JVM at startup before Storm deploys the targeted bolts / spouts and defines the executors?
IMHO if you define only Storm components in a Spring configuration it should work (startup configuration for the topology then storm only manages the objects) but if you rely on Spring to manage other components (it seems so with spring-jms), then it could become messy on topology rebalances for instance (singleton per worker / jvm? Or the whole topology?).
It is up to you to decide if it is worth the trouble, my concern with a Spring configuration is that you easily forget the storm topology (it seems it is one JVM but can be many more). Personally I define my own singletons per class-loader (static final for instance or with double check locking if I need deferred instanciation), as it does not hide the (medium-high) complexity.
I realize that this is very after the fact, but did you think of using Apache camel for the JMS connection handling? Camel isn't IOC or DI, but it does model enterprise integration patterns. Maybe that's what you are (were?) looking for?
Nick.
Maybe this tutorial can help you.
http://spring.io/guides/gs/messaging-stomp-websocket/
I am using java annotation based configuration for initializing ehcache based caching, with Spring 3.1.
Here is the sample code...
#Configuration
#EnableCaching
public class EhcacheConfig implements CachingConfigurer {
.....
#Bean
public CacheManager cacheManager() {
.....
EhCacheManagerFactoryBean bean = new EhCacheManagerFactoryBean();
bean.setCacheManagerName(CACHE_MANAGER);
bean.setShared(Boolean.TRUE);
File file = new File(property + Constants.Slash + EHCACHE_XML);
bean.setConfigLocation(new FileSystemResource(file));
try {
bean.afterPropertiesSet();
} catch (Exception e) {
throw new RuntimeException(e);
}
EhCacheCacheManager cm = new EhCacheCacheManager();
cm.setCacheManager(bean.getObject());
return cm;
}
public KeyGenerator keyGenerator() {
return new DefaultKeyGenerator();
}
}
There is a valid ehcache.xml with 1 cache declared in it.
This is all the configuration that I have for initializing ehcache with Spring. There is no XML based initialization in the application.
At runtime, I have noticed that cacheManager() is initialized, as expected. After its successful execution, the code fails to complete the initialization by erring out in:
CachingInterceptor.afterPropertiesSet() ->
if (this.cacheManager == null) {
throw new IllegalStateException("'cacheManager' is required");
}
I have done some investigation.
It appears that the problem occurs when CachingInterceptor is being initialized by ProxyCachingConfiguration.
ProxyCachingConfiguration is derived from AbstractCachingConfiguration.
AbstractCachingConfiguration has a method called:
#PostConstruct
protected void reconcileCacheManager()
This method is not invoked. Had it been invoked, the cacheManager instantiated in EhcacheConfig.cacheManger() would have been setup correctly for used by the CacheInterceptor.afterPropertiesSet().
I do not understand the reason why reconcileCacheManager() is not invoked before CacheInterceptor.afterPropertiesSet() is invoked.
Am I missing something? Can some one help me with the problem that I am facing?
Thank you.
First, you might consider extracting the initialization of the EhCacheManagerFactoryBean to its own #Bean method.
This way you can simply instantiate, configure, and return the FactoryBean without having to invoke afterPropertiesSet() yourself. This ensures that the object is a properly-managed Spring bean and that it can receive any other callbacks in might register for (like DisposableBean#destroy()) in this particular case.
Assuming that new #Bean method is named "ecmfb", you can simply call ecmfb().getObject() from within your cacheManager() method, and you'll be guaranteed at that point that the FactoryBean contract (i.e. afterPropertiesSet()) has been satisfied.
Second, you might care to know that your #Bean methods can throw any exception you like. So for example if you did not choose to extract the FactoryBean as I suggest above, you could still simplify the situation by declaring a 'throws Exception' clause on your cacheManager #Bean method. This will save you the current try/catch noise.
Finally, to address why the #PostConstruct method is not being called, let me ask how you're bootstrapping the Spring container. If you're working with AnnotationConfig(Web)ApplicationContext, the CommonAnnotationBeanPostProcessor should be registered by default. The same is true if you're using or . CABPP is responsible for the detection and processing of annotations like #PostConstruct, #PreDestroy and others. Please provide a bit more information about your bootstrapping approach and we'll go from there.