How to make SmartLifeCycle bean start before SpringCamelContext? - spring

I'm using camel in my application and I want to create a SmartLifeCycle bean which starts before the CamelContext.
In order to do that, I implemented a custom SmartLifeCycle bean with a phase of : Integer.MAX_VALUE - 101.
SpringCamelContext has a phase of : Ordered.LOWEST_PRECEDENCE
On my computer its working great, my bean is started before the SpringCamelContext bean.
But once I deploy on my servers, the SpringCamelContext is started before.
Any idea why ? And how can I make sure my bean is started before ?
Thanks
Edit
Seems like SpringCamelContext is a "LifeCycle" bean and not a "SmartLifeCycle" bean. According to the documentation :
<p>Any {#code Lifecycle} components within the context that do not also
implement {#code SmartLifecycle} will be treated as if they have a phase
value of 0. That way a {#code SmartLifecycle} implementation may start
before those {#code Lifecycle} components if it has a negative phase value,
or it may start after those components if it has a positive phase value.
But I still don't know how to fix this (how can I know if using a negative value would not create some sort of side effects ?) And why is it working on my local computer ?
Edit 2:
The difference was in the management configuration.
If I'm using a different port for the management port, and this use another configuration : DifferentManagementContextConfiguration.
On startup, the method "afterSingletonsInstantiated()" is called on the Configuration and it creates a managementContext which is refreshed immediatly.
This context is refreshed before the main ApplicationContext refresh and before the LifeCycleProcessor is started.
This refresh is initializing SpringCamelContext due to the ContextRefreshedEvent listener in it.
I'm still looking for something to fix this ...

This is a known bug which has been fixed in 2.24.1 camel version

Related

Best way to disable #KafkaListeners

My application contains several #KafkaListeners.
When I run the app locally or run some #SpringBootTests (without kafka), I got my log polluted with
[org.apache.kafka.clients.NetworkClient] - [Consumer clientId=consumer-app-1, groupId=app] Bootstrap broker localhost:9092 (id: -1 rack: null) disconnected
For such cases, I would like to disable listeners, as they are useless anyways.
I know I can do it with
#KafkaListener(... autoStartup = "${consumer.auto.start:false}")
but adding this property to all of the consumers feels cumbersome.
Unfortunately, there is no general property like spring.kafka.consumer.group-id that would affect all the consumers.
Is there a better way to achieve the same?
Thanks
If you are not interested in Spring for Apache Kafka activity in some of your Spring Boot tests, just consider to exclude its auto-configuration:
#SpringBootTest
#EnableAutoConfiguration(exclude=KafkaAutoConfiguration.class)
This way the #EnableKafka won't be applied to your application context and no KafkaListenerContainers are going to be registered to start on context refresh.
UPDATE
Another way, for the reason you explained, a respective configuration property, which you can add to application.properties, which could be a test scope-specific, or profile-based. See more info in Spring Boot docs: https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/reference/html/using-boot-auto-configuration.html#using-boot-disabling-specific-auto-configuration
The property name is spring.autoconfigure.exclude - you have to specify fully-qualified class name. Therefore for your use-case it is:
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.kafka.org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration
See also how to have test-specific application.properties or divided by the profile: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.profiles

Exception BeanFactory must be set on AnnotationAsyncExecutionAspect to access qualified executor

I created a public method with #Async and I also have a bean for that class in which this method is created. But when I am calling it , it is not behaving async and getting blocked. Then I created a executor in application-bean and used #Async(value = “executorname”), even this is not working and on every call I am getting “beanFactory must be set on AnnotationasyncExecutionAspect to access qualified executor”.
in my application bean.xml I have
there are then few executors and schedulers and I want to use one new executor.
Please tell me how to get away with this error and get async behaviour
Thanks in advance
I was experiencing exactly the same issue and find out a solution I hope works for you (https://jira.spring.io/browse/SPR-10276).
Apparently if you unabled Spring's AspectJ aspects into your project (by using aspectj-maven-plugin, for instance), Spring's Async mechanism can be placed twice in your bean, one by aspectj and other by proxy. But, since it doesn't know it, AnnotationAsyncExecutionAspect will not be injected with a BeanFactory and you will see the assertion exception you had.
To fix it, you must instruct Spring's Async mechanism to use the AspectJ support. Do this incluing this in your Application Context.
<task:annotation-driven mode="aspectj"/>
I hope I could be of any help.
For annotation based configuration you can use #EnableAsync(mode = AdviceMode.ASPECTJ)

Bitronix + Spring tests + Different spring profiles

I have several tests which all extends the same root test which define the Spring test application context. One of my test use a different profile so I have annotated the child class with #ActiveProfiles("specialTestProfile"), this profile create a special mock bean which is injected in the context. I want to clear my context before and after executing this test, but I didn't find the correct way to do it. I know that the Spring test framework does some context caching and that in my case I should have two different context and it should not be necessary to reload the context but it is not working because of bitronix which generate this strange error if I don't clean the context:
Caused by: bitronix.tm.resource.ResourceConfigurationException: cannot create JDBC datasource named unittestdb
at bitronix.tm.resource.jdbc.PoolingDataSource.init(PoolingDataSource.java:57)
at sun.reflect.GeneratedMethodAccessor404.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1608)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1549)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1479)
... 62 more
Caused by: java.lang.IllegalArgumentException: resource with uniqueName 'unittestdb' has already been registered
at bitronix.tm.resource.ResourceRegistrar.register(ResourceRegistrar.java:55)
at bitronix.tm.resource.jdbc.PoolingDataSource.buildXAPool(PoolingDataSource.java:68)
at bitronix.tm.resource.jdbc.PoolingDataSource.init(PoolingDataSource.java:53)
... 68 more
Even if I reload the context for each test class (by annotating my parent class with #DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS), I still get the error above at some point... do you have any idea how to solve this problem?
Without seeing your exact configuration for the PoolingDataSource, I cannot know exactly how to solve your issue.
However, it appears that you can likely solve this issue by creating your PoolingDataSource with a unique name by invoking the setUniqueName() method (in an #Bean method if you're using Java config) or by setting the uniqueName property (if you're using XML config). How you generate the unique name depends on the configuration style you are using.
If you do not set a unique name for each ApplicationContext that creates the PoolingDataSource bean, you will continue to see the exception telling you that a second pool cannot be created with the "unittestdb" name since it already exists. The reason is that the init() method in PoolingDataSource delegates to ManagementRegistrar.register() which registers an MBean under the unique name, and the same MBeanServer is used for all tests within the same JVM process (i.e., for all tests in your suite).
Instead of generating a unique pool name per application context, another option might be to disable the use of JMX by setting the bitronix.tm.disableJmx property to false. Consult the isDisableJmx() and setDisableJmx() methods in bitronix.tm.Configuration for details.

How to refresh the Spring context when using CXF?

We have a web application that uses Spring (3.0.5) and CXF (currently 2.4.2 for various reasons but upgrading is an option if that makes any difference) and is deployed on Tomcat.
The application is initialized using the org.springframework.web.context.ContextLoaderListener.
Starting and shutting the application down works like a charm but if I try to refresh the Spring application context, using
((ConfigurableApplicationContext)applicationContext).refresh();
I run into problems. The application context first destroys all its beans (including CXFBusImpl, or rather its subclass SpringBus). SpringBus however calls close() on its application context - leading to a NullPointerException when the application context shortly after tries to close its bean factory:
java.lang.NullPointerException
at org.springframework.context.support.AbstractRefreshableApplicationContext.closeBeanFactory(AbstractRefreshableApplicationContext.java:152)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:124)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:467)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:397)
Is there anything I can do to avoid this (other than modifying CXF)? If I skip CXF everything works.
I don't think you can tell CXF not to work that way. What you could do though is to isolate the parts of your application that need restarting into their own context that you build and and tear down as you choose without involving the main context over much. Perhaps you'd do that with a ClassPathXmlApplicationContext, though there are a few choices. I think you'll be setting the outer context as the parent of the inner, and referring to outer beans with XML-config syntax like:
<ref parent="foo" />
You'll then need to create some way of proxying the activity with CXF in the outer context to the beans in the inner context. This is the tricky part, as it is usually considered bad form for references to go that way round. You'll probably have to have some kind of registry/proxy in the outer context that (relevant) inner beans connect to as part of their creation/init process (and deregister from at tear-down). You'll also have to decide how to handle the case where a request needs to be served when there is no inner context. Tricky, especially if you want to do it elegantly...

tomcat7, spring 3.0.5, errors, contexts

org.springframework.web.context.ContextLoader, in the event of an exception, does:
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
which looks quite useful, except the servlet context object it is making this call on is not connected, in any way that I can find, to the return value from Tomcat.addWebapp. So when I go looking for this attribute to see whether the startup worked right, I'm thwarted.
Is there a way to connect these two contexts?
Tomcat's documentation says you can get ServletContext from web-app's Context:
ServletContext getServletContext()
See JavaDoc on Context.

Resources