#AutoConfigureBefore does not trigger - spring-boot

The intention of this project is to create hooks into Spring Boot's lifecycle, right before Liquibase executes its database schema changesets. The hooks will eventually be used to start / stop a Docker (or Docker-like) container with a Postgres instance. The project must be able to deal with:
tests run from the IDE (IntellIJ, STS)
tests run with Maven Surefire
spring-boot:run
executable JAR
integration test on CI server
Given the above limitations, the best approach appeared to be to have an Auto-Configuration and instructing it to run before Liquibase.
The Auto-Configuration class has been annotated:
#ConditionalOnProperty(prefix = "docker_42",
name = "enabled", matchIfMissing = false)
#AutoConfigureBefore({LiquibaseAutoConfiguration.class })
public class Docker42AutoConfiguration {
spring.factories has a single entry:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
nl._42.autoconfig.Docker42AutoConfiguration
The entire (stripped down) project can be found here: https://github.com/robert-bor/auto-configuration-question
The results can be verified by either:
setting up the project in your IDE & running a test, or
executing spring-boot:run
In Spring Boot's log you will see Liquibase executing before the custom AutoConfiguration.
Note that various other routes have been tried (ApplicationListener, RunListener), but none played nice with all the required in-roads.
Pointers as to why #AutoConfigureBefore does not work in my project would be very much appreciated.

You're trying to apply a configuration semantic to some runtime constraints. Auto-configuration is about providing bean definitions in the context, something that will ultimately create bean instances that you can wire in your components.
Assume that you need bean Foo in order to auto-configure something. You need to make sure that FooAutoConfiguration runs before yours so that the context has a chance to contain a bean definition for Foo. That is explained in quite details in our last Devoxx university.
The documentation you referenced in that comment does not imply runtime constraints in any way:
Hint for that an auto-configuration should be applied before other specified auto-configuration classes.
applying the auto-configuration does not mean that the beans created by that configuration will effectively start before the beans created by another configuration. In other words, what you've done is making sure that your auto-configuration configures the context before the liquibase auto-configuration has a chance to do so. It does not imply, in any way, that the beans that will be created (we're not there yet) will be created in that order.
If that's what you want to do and you have no bean dependency, you can just forget about all that and create a bean lifecycle dependency instead. It can be tricky because liquibase may or may not be there but we essentially do that for Hazelcast (see the auto-configuration). Basically we need to make sure that Hazelcast is started before the JPA container is started in case it wants to use Hazelcast as second level cache.

Related

How to disable Javers for integration tests?

I am using Javers 3.11.2 with Spring Boot 1.5.17. When I am trying to run integration tests on an embedded database I still see that Javers tables are getting created each time.
Is there a way I can disable Javers during these tests so that these tables will not be created each time?
There is the easy way, put:
javers:
sqlSchemaManagementEnabled: false
in your application-test.yml. See https://javers.org/documentation/spring-boot-integration/
Disclaimer: I've never used Javers.
In general, disabling something in "integration tests" means that you don't want to load some beans (of Javers in this case).
This means in turn that you have to exclude them from the list of configurations spring boot works with.
If you're using javers autoconfiguration module, it has to provide in its own "spring.factories" file (can be found inside the jar) a file for autoconfiguration.
Find its java code and see whether it has some "#Conditional on something (property beans, etc.)" If it has than create a profile for integration test that will configure the beans in a way that conditional in javers won't pass and the bean won't be created as a consequence
If it doesn't have a conditional on something like this, you'll have to exclude the whole configuration. Its usually can be done by annotation #SpringBootApplication(exclude=<JaversAutoconfiguration goes here>
This will, however, turn it off also for production usage, which is obviously not something that you want. So for "production" profile, you'll have to import it as a regular configuration (not an autoconfiguration), for integration test profile you won't need this.

Facing issue while integrating CustomSpringUtilityJar in SpringBootApplication

I am having two spring applications and the details as follows:
1). Application One: This is a utility jar and its purpose is for CRUD operations on a specific table. We are using spring data jpa and hibernate for achieving the same. Since it is an utility this will not run alone
and it will be incuded as dependency to other applications. In this case we are using Application2 for that.
2) Application Two: This is a web service application made of Spring boot using yml configuration and also using Application One for CRUD operations on that table.
Issue: While running application Two(i.e. Spring Boot Application) , I am getting the error message related to the the components, repositories and entities of Application one,
as the bean is not registered in the configuration. Following are the approaches I used to solve thes:
Approach 1: In application 2, along with #SpringBootApplication annotation:
added Component Scan, Enity Scan and Enable JPA repositories and in these three annotations we have to include the package of both Application1 and Application2.
This scenario is working fine.
Note: I am not using Approach 1 as I have to make these three changes in every service applications which is going to use this utility jar. So decided to proceed with Approach2
Approach 2: Created an custom annotation like #UtilJar annotation and configuration class in Application1 and in that configuration class, added the three
annotations along with the configuration annotation.
In this case, while adding the custom annotation(created from Application1) in Application2, all the components and repositories registered in Application1 is registered and error is
showing related to the repositories registered in Application 2 . The only way I can solve on this is to give EnableJPARepository in Application2 which again similar to
Approach1.
So the root cause what I understood is because of #EnableJPARepository annotation given in the Application1 prompted for this error in Application2. Can anyone suggest me how to proceed on this.

How can I exclude beans from certain contexts in Spring?

I have a project that mixes Spring Batch with Spring Web. It's primarily a Spring Batch project that runs jobs, but we've got a few REST endpoints on there, and the Spring Batch Admin Manager module as well. Our base project has its own (primary) application context, but the Admin Manager project we've included in the POM effectively creates its own context as well.
One of my classes is a #RestController which means it automatically gets instantiated by both contexts. This controller has a couple of #Autowired dependencies, which is fine when the first context runs because it finds them all. But when the second context runs, it fails to find those dependencies and so the app fails to launch properly.
But because this second context is created automatically, behind the scenes, from that aforementioned Sping Batch Admin Manager project, I don't really have control over it. Is there some way I can manually specify in my #RestController that it should be excluded from all but my primary context?
You can use one of the #Conditional annotations
E.g. #ConditionalOnClass or #ConditionalOnMissingClass or #ConditionalOnBean or #ConditionalOnMissingBean
So your controller just checks whether there is classes/beans which is required to run the endpoint
Read more here

In a spring-boot application, is it possible to change the dependency injection by external configuration?

I am building an application to send email but I want to turn off the actual send and just have it output to a log file in non-prod environments. The class that actually sends mail is a Spring injected dependency, so my thought was just to build a no-op implementation to swap out with the actual implementation.
Second requirement, I don't want to create separate builds for different environments. I want to create one and promote it through dev -> test -> prod.
This seems like a common use case for dependency injection but I don't see how to externally configure it. If I want to do it this way do I need to abandon annotation based injection and use xml instead?
You can add #Profile("PRODUCTION") on your production #Bean and #Profile("!PRODUCTION") on your non-production #Bean. Then you can specify the active profile(s) at runtime:
java -jar bootApp.jar --spring.profiles.active=PRODUCTION, ...

Share spring container between test application and embedded tomcat

We are using cucumber-jvm to write an integration test layer in our application. One of the challenges we are finding is managing the database between the tests and the web application.
A typical scenario is that we want to persist some entities in a Given step of a scenario, then perform some actions on the user interface that may, in turn, persoist more entities. At the end, we want to clean the database. Because the cucumber-jvm tests are in one jvm and the web application is running in another jvm we cannot share a transaction (at least in a way of which I am aware) so the database must be cleaned manually.
My initial thought was to use an Embedded Tomcat server running off of an embedded in-memory database (HSQLDB) in the same JVM as the cucumber-jvm test. This way we might be able to share a single spring container, and by extension a single transaction, from which all objects could be retrieved.
During my initial tests it looks like Spring gets loaded and configured twice: once when the test starts and the cucumber.xml is read, and a second time when the embedded tomcat starts and the web application reads its applicationContext.xml. These appear to be in two completely separate containers because if I try to resolve an object in one container that is specified in the other container then it doesn't resolve. If I duplicate my configuration then I get errors about duplicate beans with the same id.
Is there a way that I can tell Spring to use the same container for both my test application and the embedded tomcat?
I'm using Spring 3.2.2.GA and Embedded Tomcat 7.0.39 (latest versions of both libraries).
Am I crazy? Do I need to provide more technical details? Apologies if I use some incorrect terminology.
Thanks
p.s. If my problem seems familiar to you and you can suggest an alternative solution to the one I am trying, please let me know!
Jeff,
It is normal that spring is loaded twice. There are two places where two spring contexts are created:
In the servlet container listener org.springframework.web.context.ContextLoaderListener that is configured in web.xml. This one reads its configuration from the file set by the context-param contextConfigLocation.
In the implementation of ObjectFactory provided by cucumber-spring plugin cucumber.runtime.java.spring.SpringFactory. This one reads its configuration from cucumber.xml.
The two spring contexts are totally different and their instances are kept in two different places. As a servlet context attribute for the former and kept by the JavaBackend for the latter.
When starting the embedded tomcat, it is possible to get access to the servlet context and thus set ourself the spring context used bt tomcat with the one from cucumber. But, spring has a special class called WebApplicationContext for context used in a servlet container. The cucumber SpringFactory on other hand creates its context through ClassPathXmlApplicationContext. So unless there is a way to specify the type of application context from the xml config, we will have to provide an ObjectFactory that shoots a WebApplicationContext.
What we can do is to have two web.xml. One for the normal and one for the test. For the test, we use our version of the ContexLoader listener.

Resources