Conditional auto-wiring in Spring boot application - spring-boot

I have a spring boot application for which I want to create separate profiles for external and embedded tomcat. How can I check in my method which profile is active so that I can run the code based on particular profile.
I came up with some code as shown below.
if("${spring.active.profile}".contains("external_tomcat_profile")) {
//do something;
}else{
//another thing;
}
The above code does not work. How can I implement this functionality? or Is there and better way of doing this?
And I am using two profiles one "test" for testing and another either embedded or external tomcat, so is it correct to use this condition
if("${spring.active.profile}".contains("external_tomcat_profile"))

You can use the Environment bean for that
#Autowired
Environment env;
public void aMethod() {
String[] activeProfiles = env.getActiveProfiles();
}

Related

How to prevent controller endpoints only available for a certain lifecycle environment in spring boot

Delete an article using DELETE /articles/:id
Delete all articles using DELETE /articles/
How can I make deletion support available only in dev environment and prevent it for test, staging, production environments in spring boot
First thing that comes to my mind would be adding a DeletionController which is created either based on a property or, in your case, on the active profile.
Something like:
#Profile("dev")
#RestController
public class DeletionController {
#DeleteMapping("articles")
public void deleteAll() {
//delete all articles
}
#DeleteMapping("articles/{id}")
public void delete(#PathVariable Integer id) {
//delete article for given id
}
}
Doing so Spring will only instantiate the DeletionController when the dev profile is active making the related endpoints available only in that case. You also have the possibility to have it active/inactive with more complex conditions like #Profile("dev & staging") or #Profile("dev & !production"). You can control the active profiles in your property file with the property spring.profiles.active.
The property approach would be using, instead of #Profile, the annotation #ConditionalOnProperty properly configured.

Spring boot test mutliple projects with same bean name

I have multiple spring projects as part of a single umbrella project. Two of them are AuthServer and BackendApplication. AuthServer, as name suggests is used only for auth purposes and rest is handled by BackendApplication. Now I am trying to write tests inside BackendApplication that also need to use auth related work. For that I have added AuthServer as a test dependency to BackendApplication. Now the problem is that, both projects have beans names Utility because of which I get DuplicateBeanException when I am including both contexts in my test. But I can disable any of them as they are necessary. Is there a way around it?
Could you name your beans, for example:
#Bean(name = "my-utility-1")
public Utility createUtility1() {
return new Utility();
}
// or
#Component(value = "my-utility-2")
public class Utility {
...
}
and refer to them by #Qualified
#Autowired #Qualified("my-utility-1")
private Utility myUtility;
Not related to your question, but i think you can mock AuthServer when testing BackendApplication.

How to Integration Test Spring Shell v2.x

The new Spring Shell docs don't seem to provide any examples of how to integration test CLI commands in a Spring Boot context. Any pointers or examples would be appreciated.
The method Shell#evaluate() has been made public and has its very specific responsibility (evaluate just one command) for exactly that purpose. Please create an issue with the project if you feel like we should provide more (A documentation chapter about testing definitely needs to be written)
Here is how I got this working.
You first need to override the default shell application runner to avoid getting stuck in the jline loop. You can do this by defining your own such as:
#Component
public class CliAppRunner implements ApplicationRunner {
public CliAppRunner() {
}
#Override
public void run(ApplicationArguments args) throws Exception {
//do nothing
}
}
Note that you will have to associate this custom Application runner against a "Test" profile so it overrides only during integration testing.
If you want to test a shell command "add 1 3", you then can write a test like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes =CliConfig.class)
public class ShellCommandIntegrationTest {
#Autowired
private Shell shell;
#Test
public void runTest(){
Object result=shell.evaluate(new Input(){
#Override
public String rawText() {
return "add 1 3";
}
});
DefaultResultHandler resulthandler=new DefaultResultHandler();
resulthandler.handleResult(result);
}
}
Note that the above test does not Assert anything. You will probably have to write your own little implementation of the ResultHandler interface that deals with parsing/formatting of the result so that it can be asserted.
Hope it helps.
Spring Shell 2.0.1 depends on Spring Boot 1.5.8, which in turn depends on Spring Framework 4.3.12. This makes researching how to implement tests challenging, since the latest version of Spring Shell does not depend on the latest versions of other Spring libraries. Take a look at my example project, sualeh/spring-shell-2-tests-example which has example unit, functional and integration tests for a sample Spring Shell application.

Reload property value when external property file changes ,spring boot

I am using spring boot, and I have two external properties files, so that I can easily change its value.
But I hope spring app will reload the changed value when it is updated, just like reading from files. Since property file is easy enough to meet my need, I hope I don' nessarily need a db or file.
I use two different ways to load property value, code sample will like:
#RestController
public class Prop1Controller{
#Value("${prop1}")
private String prop1;
#RequestMapping(value="/prop1",method = RequestMethod.GET)
public String getProp() {
return prop1;
}
}
#RestController
public class Prop2Controller{
#Autowired
private Environment env;
#RequestMapping(value="/prop2/{sysId}",method = RequestMethod.GET)
public String prop2(#PathVariable String sysId) {
return env.getProperty("prop2."+sysId);
}
}
I will boot my application with
-Dspring.config.location=conf/my.properties
I'm afraid you will need to restart Spring context.
I think the only way to achieve your need is to enable spring-cloud. There is a refresh endpoint /refresh which refreshes the context and beans.
I'm not quite sure if you need a spring-cloud-config-server (its a microservice and very easy to build) where your config is stored(Git or svn). Or if its also useable just by the application.properties file in the application.
Here you can find the doc to the refresh scope and spring cloud.
You should be able to use Spring Cloud for that
Add this as a dependency
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter', version: '1.1.2.RELEASE'
And then use #RefreshScope annotation
A Spring #Bean that is marked as #RefreshScope will get special treatment when there is a configuration change. This addresses the problem of stateful beans that only get their configuration injected when they are initialized. For instance if a DataSource has open connections when the database URL is changed via the Environment, we probably want the holders of those connections to be able to complete what they are doing. Then the next time someone borrows a connection from the pool he gets one with the new URL.
Also relevant if you have Spring Actuator
For a Spring Boot Actuator application there are some additional management endpoints:
POST to
/env to update the Environment and rebind #ConfigurationProperties and log levels
/refresh for re-loading the boot strap context and refreshing the #RefreshScope beans
Spring Cloud Doc
(1) Spring Cloud's RestartEndPoint
You may use the RestartEndPoint: Programatically restart Spring Boot application / Refresh Spring Context
RestartEndPoint is an Actuator EndPoint, bundled with spring-cloud-context.
However, RestartEndPoint will not monitor for file changes, you'll have to handle that yourself.
(2) devtools
I don't know if this is for a production application or not. You may hack devtools a little to do what you want.
Take a look at this other answer I wrote for another question: Force enable spring-boot DevTools when running Jar
Devtools monitors for file changes:
Applications that use spring-boot-devtools will automatically restart
whenever files on the classpath change.
Technically, devtools is built to only work within an IDE. With the hack, it also works when launched from a jar. However, I may not do that for a real production application, you decide if it fits your needs.
I know this is a old thread, but it will help someone in future.
You can use a scheduler to periodically refresh properties.
//MyApplication.java
#EnableScheduling
//application.properties
management.endpoint.refresh.enabled = true
//ContextRefreshConfig.java
#Autowired
private RefreshEndpoint refreshEndpoint;
#Scheduled(fixedDelay = 60000, initialDelay = 10000)
public Collection<String> refreshContext() {
final Collection<String> properties = refreshEndpoint.refresh();
LOGGER.log(Level.INFO, "Refreshed Properties {0}", properties);
return properties;
}
//add spring-cloud-starter to the pom file.
Attribues annotated with #Value is refreshed if the bean is annotated with #RefreshScope.
Configurations annotated with #ConfigurationProperties is refreshed without #RefreshScope.
Hope this will help.
You can follow the ContextRefresher.refresh() code implements.
public synchronized Set<String> refresh() {
Map<String, Object> before = extract(
this.context.getEnvironment().getPropertySources());
addConfigFilesToEnvironment();
Set<String> keys = changes(before,
extract(this.context.getEnvironment().getPropertySources())).keySet();
this.context.publishEvent(new EnvironmentChangeEvent(context, keys));
this.scope.refreshAll();
return keys;
}

Configure Spring Boot application to use MongoDB connection uri provided in environment variable

I would like to configure the connection-uri to my MongoDB through an environment variable. This way, I can set different values on localhost or if the Spring Boot application is running in a cloud.
I have included mongodb in my build.gradle file:
dependencies {
compile 'org.springframework.cloud:spring-cloud-spring-service-connector:1.2.2.RELEASE'
compile("org.springframework.boot:spring-boot-starter-data-mongodb")
...
}
To work locally, I have currently set the spring.data.mongodb.uri=mongodb://... in applications.properties but I would rather like to have that value read from an environment variable. How can I achieve this?
I have read articles about Spring Boot and Cloud suggesting extending the AbstractCloudConfig somehow like this:
public class CloudConfig extends AbstractCloudConfig {
#Bean
public MongoDbFactory documentMongoDbFactory() {
return connectionFactory().mongoDbFactory();
}
}
But I assume this wouldn't work with environment variables and working locally.
You should use profiles to do that.
Read about profiles
Read how to use Profiles
How to Set Profiles

Resources