Spring boot get configurations - spring-boot

I want to exclude all unused configurations in a Spring boot application. The problem is that I do not know which one are used and which are not.
Is it possible to get in run time all configurations which are used in a Spring boot application and to write them into a file which is read from #EnableAutoConfiguration exclude annotation? If it is no is there another approach to do something like this?

I personnally believe you should to go along with Spring-Boot since most (if not all) autoconfigurations are only enabled when some class(es) is found on the classpath.
#Import the Autoconfiguration classes yourself
I don't know if it's a great idea, but still you could skip the AutoConfiguration altogether by not using #SpringBootApplication and/or #EnableAutoConfiguration.
Instead, define your Spring Boot entry point like a normal bean with #ComponentScan AND #import the Autoconfiguration classes of your choice:
#ComponentScan(excludeFilters = #Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class))
#Import({EmbeddedServletContainerAutoConfiguration.class})
public class Launcher {
public static void main(String[] args) {
SpringApplication.run(Launcher.class, args);
}
}
Instead of having AutoConfiguration kickstart the configurations (based on #Conditional presence of some classes on the classpath), you can import the one you want.
Something like this would get you going (for a minimal web container):
#Import({EmbeddedServletContainerAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
DispatcherServletAutoConfiguration.class,
ConfigurationPropertiesAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class,
WebMvcAutoConfiguration.class,
MessageSourceAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class})
Note that this may easily lead you to have some basic functionalities disabled until you realize it and add its AutoConfiguration.
You'll find the list of AutoConfiguration classes in spring-boot-autoconfigure-xxx.jar/META-INF/spring.factories:
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.hornetq.HornetQAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\
org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\
org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\
org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.velocity.VelocityAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration
# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.velocity.VelocityTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.JspTemplateAvailabilityProvider

Related

Load custom properties file in Spring Boot MVC Main

I have created a myApp.properties in resources folder location and mentioned the server.port in this file.
myApp.properties
myApp.server.port=8020
Now I want to read load this property into my application. But I have to read this before I actually a server.
Here I am trying to do like this
#SpringBootApplication
#ComponentScan(basePackages = {"com.myorg.myapp" })
#EnableConfigurationProperties
#PropertySource("classpath:myApp.properties")
#Component
public class MyAppApplication {
#Value("${myApp.server.port}")
private static String serverPort;
public static void main(String[] args) throws Exception{
try {
SpringApplication appCtxt = new SpringApplication(MyAppApplication.class);
appCtxt.setDefaultProperties(Collections
.singletonMap("server.port", serverPort));
appCtxt.run(args);
} catch (Exception e) {
e.printStackTrace();
}
}
But serverPort is coming as null.
I also tried to create a separate Config file like this but it can't be accessed in static main
#Configuration
#PropertySource("myApp.properties")
#ConfigurationProperties
public class MyAppConfig {
#Value("${myApp.server.port}")
private String serverPort;
/**
* #return the serverPort
*/
public String getServerPort() {
return serverPort;
}
}
Any suggestion would be helpful.
Spring boot injects properties during the initialization of the application context.
This happens (gets triggered) in the line:
appCtxt.run(args);
But you try to access the property before this line - that why it doesn't work.
So bottom line, using "#Value" in the main method doesn't work and it shouldn't.
Now from the code snippet, it looks like you could merely follow the "standards" of spring boot and create the file application.properties with:
server.port=1234
The process of starting the embedded web server in spring boot honors this property and bottom line it will have the same effect and Tomcat will be started on port 1234
Update 1
Based on OP's comment:
So, how can I have multiple application.properties.
In the Spring Boot's documentation it is written that application.properties are resolved from the classpath. So you can try the following assuming you have different modules A,B,C and web app D:
Create src/main/resources/application.properties inside each of 4 modules and pack everything together. The configuration values will be merged (hopefully they won't clash)
If you insist on naming properties A.properties, B.properties and C.properties for each of non-web modules, you can do the following (I'll show for module A, but B and C can do the same).
#Configuration
#PropertySource("classpath:A.properties")
public class AConfiguration {
}
Create in Module A: src/main/resources/A.properties
If you need to load the AConfiguration automatically - make the module A starter (using autoconfig feature of spring-boot):
Create src/resources/META-INF/spring.factories file with the following content:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
<package_of_AConfiguration>.AConfiguration
Also this has been the requirement to separate C from entire bundle where it might run as bundle for some and as a separate for some others
Although I haven't totally understood the requirement, but you can use #ConditionalOnProperty for configuration CConfiguration (that will be created just like AConfiguration.java in my previous example) but this times for module C.
If the conditional is met, configuration will run and load some beans / load its own properties or whatever. All in all conditionals (and in particular Profiles in spring) can help to reach the desired flexibility.
By default, the application.properties file can be used to store property pairs, though you can also define any number of additional property files.
If you save myApp.server.port=8020 in application.properties, it will work fine.
To register a custome property file, you can annotate a #Configuration class with the additional #PropertySource annotation:
#Configuration
#PropertySource("classpath:custom.properties")
#PropertySource("classpath:another.properties")
public class ConfigClass {
// Configuration
}
make sure, your class path is correct.

Reading multiple entity scan packages in spring boot application

I have Spring boot application
#SpringBootApplication
#EntityScan(basePackages = {"${scan.packages}"})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
While reading multiple entity scan packages from one property separated by comma like below?
scan.packages=com.mycompany.model.package1 ,
com.mycompany.model.package2
I got this exception :
java.lang.IllegalArgumentException: Unknown entity:
com.mycompany.model.package2.Myclass
You can scan multiple Entity like this
#EntityScan(basePackages= {"scan.packages1","scan.packages2"})
This should work
#EntityScan(basePackages = {"#{'${scan.packages}'.split(',')}"})
According to the EntityScan annotation javadoc there are three ways to define the packages where to scan for entities:
value, alias for basePackages: #EntityScan( {"scan.packages1","scan.packages2"})
basePackages, alias for value: #EntityScan(basePackages= {"scan.packages1","scan.packages2"}).
basePackagesClasses, the type safe version: #EntityScan(basePackages=E.class}.Where E was a marker interface just to underline the Entities. Please see the code bellow. It could also be an annotation.
interface E {
}
#Entity
public class Glass implements E {
// Typical code to be added here
}
Or as an annotation:
#Retention(RetentionPolicy.RUNTIME)
#interface E {
}
#Entity
#E
public class Glass implements E {
// Typical code to be added here
}
From my perspective I would choose either using directly value or basePackageClasses. If I can something easier to read I would do that, and I think that is what value does, while the basePackageClasses introduces the added benefit of type safety and I can see multiple reasons to go for that. It all depends on the context.
I hit the same problem and posted an issue on the Spring Boot issue tracker about it.
The answer was that
... As shown by #25436, #EntityScan did support property solution in 2.3 so, if we decide to reinstate support, we might want to consider how to handle multi-value properties.
So it appears some Spring Boot version did support it, but then the support was dropped again... for more info, check the Spring Boot issue linked in the quote.
try this :
#EntityScan(basePackages= {"${scan.packages1","scan.packages2}"})

SpringBoot scanBasePackages not working in multi module project

I have the following maven project structure:
eu.arrowhead
common
repository
-AJpaRepository.class
orchestrator
controller
-AController.class
OrchestratorApplication
other_modules...
Where two of the modules are common, and orchestrator. Common is a dependency for the Orchestrator module. The JpaRepositoryClass is annotated with #Repository.
In the controller class I use the constructor autowiring to get a copy of the repository:
private final AJpaRepository serviceRepo;
#Autowired
public AController(AJpaRepository serviceRepo){
this.serviceRepo = serviceRepo;
}
And finally, in the Application class, I use scanBasePackages, to pick up the components from the common module:
#SpringBootApplication(scanBasePackages = "eu.arrowhead")
public class OrchestratorApplication {
public static void main(String[] args) {
SpringApplication.run(OrchestratorApplication.class, args);
}
}
When I start the application, I get:
Description:
Parameter 0 of constructor in eu.arrowhead.orchestrator.controller.ArrowheadServiceController required a bean of type 'eu.arrowhead.common.repository.ArrowheadServiceRepo' that could not be found.
Action:
Consider defining a bean of type 'eu.arrowhead.common.repository.ArrowheadServiceRepo' in your configuration.
If I use scanBasePackages = {"eu.arrowhead.common"} then the application starts without an error, but I can not reach the endpoint in my controller class (getting the default 404 error). If I write scanBasePackages = {"eu.arrowhead.common", "eu.arrowhead.orchestrator"} it's the same as if only "eu.arrowhead" is there, I get the same error at startup.
Is this how this supposed to work? I highly doubt it.
Depencendies:
Common module: starter-data-jpa, starter-json, mysql-connector-java, hibernate-validator
Orch module: starter-web, the common module.
I also tried using #ComponentScan, but had the same result. What is the problem? Thanks.
You are missing #EnableJpaRepositories("eu.arrowhead") annotation to enable Spring Data JPA repository scanning.

SpringBoot junit tests with multiple TomcatServletContainerInitializer 's - BindException: Address already in use"

I have spring boot app and currently all my test are running green if ai run them separately i.e. manually, but when i run the maven package command then all test run in a serial mode i.e. after each other. What happens then is that i get:
"java.net.BindException: Address already in use".
Basically because the test are running in serial they all try to claim the socket 127.0.0.1:8081. And since the the socket is in a
TIME_WAIT state the second test is not able to claim that socket. i.e. the output of netstat -apn
netstat -apn |grep -i 8080
tcp 0 0 127.0.0.1:8080 127.0.0.1:33952 TIME_WAIT
Now i have two different profiles configured i.e. Development and Production i.e. see below:
#Configuration #Profile("Development") public class
TomcatEmbededDevelopmentTesting1Profile extends
SpringServletContainerInitializer { ... }
#Configuration #Profile("Production") public class
TomcatEmbededProductionProfile extends
SpringServletContainerInitializer { .. }
What I am looking after now is a feature to specify which customer TomcatServletContainerInitializer i can run i.e. i would have 5 different one all listening on different ports/sockets.
The problem is if I have 5 different TomcatEmbeded configuration classes that are all marked with "#Profile("Development")" then how do i tell junit to execute the one that i need. I have tired using the #SpringApplicationConfiguration , but that does not fly.
#SpringApplicationConfiguration(classes = {
...
TomcatEmbededDevelopmentTesting1Profile.class,
...
} )
then i found an article on the net that explains that there is some magic annotation #IntegrationTest("server.port:0") which is suppose to randomize that port, but that did not worked for me as well.
What is the right way to do it in spring? Any hints are greatly appreciated.
here is example of my test:
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners(listeners={ ServletTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
WithSecurityContextTestExecutionListener.class
}
)
#SpringApplicationConfiguration(classes = {
SecurityWebApplicationInitializerDevelopment.class,
SecurityConfigDevelopment.class,
TomcatEmbededDevelopmentTesting1Profile.class,
Internationalization.class,
MVCConfigDevelopment.class,
PersistenceConfigDevelopment.class,
ServerAuthenticationSuccessHandler.class,
ServerRedirectAuthenticationSuccessHandler.class,
LogicServiceRegistryPostProcessor.class
} )
#WebAppConfiguration
#EnableWebSecurity
#EnableWebSocket
#EnableGlobalMethodSecurity(prePostEnabled = true)
//#IntegrationTest({"server.port=0", "management.port=0"})
#ActiveProfiles("Development")
public class quickTest extends TestCase {
..
}
For starters, #Enable* annotations are not supported on test classes with Spring Boot 1.3.x. So delete those and move them to actual #Configuration classes.
Secondly, supplying a huge list of classes to #SpringApplicationConfiguration is a worst practice. The best practice is to define a single #Configuration class for your integration tests which uses #Import to include whatever other configuration classes you need.
Third, use #WebIntegrationTest(randomPort = true) instead of #WebAppConfiguration.
Fourth, do not extend TestCase: this is JUnit 4, not JUnit 3.
Fifth, do not include the ServletTestExecutionListener: it is only used for out-of-container integration testing, and what you are doing is in-container integration testing (i.e., in an embedded Tomcat Servlet container).
Sixth, if you need the random port number, simply inject it via #Value("${local.server.port}").
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners(
listeners = WithSecurityContextTestExecutionListener.class,
mergeMode = MERGE_WITH_DEFAULTS
)
#SpringApplicationConfiguration({
SecurityWebApplicationInitializerDevelopment.class,
SecurityConfigDevelopment.class,
TomcatEmbededDevelopmentTesting1Profile.class,
Internationalization.class,
MVCConfigDevelopment.class,
PersistenceConfigDevelopment.class,
ServerAuthenticationSuccessHandler.class,
ServerRedirectAuthenticationSuccessHandler.class,
LogicServiceRegistryPostProcessor.class
})
#WebIntegrationTest(randomPort = true)
#ActiveProfiles("Development")
public class QuickTest {
#Value("${local.server.port}")
int port;
// ...
}
So, see if that gets you any further, and read the Spring Boot Reference Manual for further details on Spring Boot's testing support.

Grails Dependency Injection Outside of Services?

I have a Grails application that needs to run a strategy that will likely be swapped out over time. I know Spring underlies Grails, so I was wondering if I had access to Spring's IoC container so that I could externalize the actual dependency in an xml file (note: I have never actually done this, but just know of it, so I may be missing something). My goal is to be able to do something like the following:
class SchemaUpdateService {
public int calculateSomething(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
IStrategy strat = (IStrategy) ctx.getBean("mystrat");
}
}
And then map the appropriate implementation in the beans.xml file. I assume this is supported in Grails. Does anyone have any documentation on how this would work? Do I really just need the Spring IoC library and it will just work? Thanks!
You define your beans in resources.xml or resources.groovy. The grails documentation is very clear about how to access the Spring application context.
You can access the application context from any Grails artefact using
ApplicationContext ctx = grailsApplication.mainContext
You can then use this to retrieve whichever beans you're interested in:
IStrategy strat = (IStrategy) ctx.getBean("mystrat")
In classes that don't have access to grailsApplication, you could use a helper such as the following to access the application context and the beans therein
class SpringUtils {
static getBean(String name) {
applicationContext.getBean(name)
}
static <T> T getBean(String name, Class<T> requiredType) {
applicationContext.getBean(name, requiredType)
}
static ApplicationContext getApplicationContext() {
ApplicationHolder.application.mainContext
}
}
However, this should only be necessary if you need to retrieve different implementations of the same bean at runtime. If the required bean is known at compile-time, just wire the beans together in resources.xml or resources.groovy
First of all, you want to define your strategy in your grails-app/conf/spring/resources.groovy:
beans = {
myStrat(com.yourcompany.StrategyImpl) {
someProperty = someValue
}
}
Then, you simply def the a property with the same name into your service:
class SomeGrailsService {
def myStrat
def someMethod() {
return myStrat.doSomething()
}
}
In any Grails artefact (such as services and domain classes), Grails will automatically give the myStrat property the correct value. But don't forget, in a unit test you'll have to give it a value manually as the auto-wiring does not happen in unit tests.
Outside of a Grails artefact, you can use something like:
def myStrat = ApplicationHolder.application.mainContext.myStrat
In Grails 2.0, Graeme et al are deprecating the use of the *Holder classes (such as ApplicationHolder and ConfigurationHolder), so I'm not quite sure what the Grails 2.0 approach would be...

Resources