Spring 4, MongoDB, using #Value in MongoConfiguration - spring

I use a MongoConfiguration class to setup my Sping 4 MongoDB. I want to read properties from application.properties so I use #Value:
....
#Configuration
#EnableMongoRepositories
#ComponentScan(basePackageClasses = {Application.class})
public class MongoConfiguration extends AbstractMongoConfiguration {
#Value("${mongodb.host}")
String mongodb_host;
#Value("${mongodb.port}")
int mongodb_port;
#Value("${mongodb.databasename}")
String mongodb_databasename;
#Override
protected String getDatabaseName() {
return mongodb_databasename;
}
#Override
public Mongo mongo() throws Exception {
return new MongoClient( mongodb_host, mongodb_port );
}
#Override
protected String getMappingBasePackage() {
return "com.example.mongodb01";
}
}
This works fine for a web application -- but when I try the same idea in a command line Java application it fails (it's as if the application.properties was found but #Value never ran). I know I am reading the applications.properties file OK. It must have something to do with the differences in running in a servlet container vs. an application but after much searching and trials I have not been able to resolve this and fix it. I would appreciate any help on this -- Thank you!
I did see a similar question and I tried adding the below to my MongoConfiguration but still had the same problem:
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}

Did you include the #PropertySource
#Configuration
#PropertySource("classpath:application.properties")
#EnableMongoRepositories
#ComponentScan(basePackageClasses = {Application.class})
public class MongoConfiguration extends AbstractMongoConfiguration {
...

Related

Spring Boot Injecting Implementations for Prod and Test

I'm new to spring boot and I'm trying to wrap my head around how to make dependency injection work for deployment and testing.
I have a #RestController and a supporting #Service. The service injects another class that is an interface for talking to Kafka. For the Kafka interface I have two implementations: one real and one fake. The real one I want to use in production and the fake in test.
My approach is to use two different configuration for each environment (prod and test).
#Configuration
public class AppTestConfiguration {
#Bean
public KafkaMessagePublisher kafkaMessagePublisher() {
return new KafkaMessagePublisherFakeImpl();
}
}
#Configuration
public class AppConfiguration {
#Bean
public KafkaMessagePublisher kafkaMessagePublisher() {
return new KafkaMessagePublisherImpl();
}
}
Then in my main application I would like to somehow load AppConfiguration.
#SpringBootApplication
public class DeployerServiceApiApplication {
public static void main(String[] args) {
SpringApplication.run(DeployerServiceApiApplication.class, args);
}
// TODO: somehow load here...
}
And in my test load the fake configuration somehow
#SpringBootTest
#AutoConfigureMockMvc(addFilters = false)
public class DeployerServiceApiApplicationTest {
#Autowired private MockMvc mockMvc;
// TODO: somehow load AppTestConfiguration here
#Test
public void testDeployAction() throws Exception {
...
ResultActions resultActions = mockMvc.perform(...);
...
}
}
I've spent the better part of a day trying to figure this out. What I'm trying to accomplish here is fundamental and should be straight forward yet I keep running into issues which makes me wonder if the way I'm thinking about this is all wrong.
Am not sure if i understand your question completely but from description i guess you wish to initialize bean based on environment. Please see below.
#Profile("test")
#Configuration
public class AppTestConfiguration {
#Bean
public KafkaMessagePublisher kafkaMessagePublisher() {
return new KafkaMessagePublisherFakeImpl();
}
}
#Profile("prod")
#Configuration
public class AppConfiguration {
#Bean
public KafkaMessagePublisher kafkaMessagePublisher() {
return new KafkaMessagePublisherImpl();
}
and then you can pass the "-Dspring.profiles.active=prod" argument while starting you application using java command or you can also specify the profile in your test case like below.
#SpringBootTest
#ActiveProfile("test")
#AutoConfigureMockMvc(addFilters = false)
public class DeployerServiceApiApplicationTest
Use spring profiles, you can annotate your test class with #ActiveProfiles("test-kafka") and your test configuration with #Profile("test-kafka").
This is pretty easy task in spring boot world
Rewrite your classes as follows:
#Profile("test")
#Configuration
public class AppTestConfiguration {
#Bean
public KafkaMessagePublisher kafkaMessagePublisher() {
return new KafkaMessagePublisherFakeImpl();
}
}
#Profile("prod")
#Configuration
public class AppConfiguration {
#Bean
public KafkaMessagePublisher kafkaMessagePublisher() {
return new KafkaMessagePublisherImpl();
}
}
This will instruct spring boot to load the relevant configuration when the "prod"/"test" specified.
Then you can start your application in production with --spring.profiles.active=prod and in the Test you can write something like this:
#SpringBootTest
#ActiveProfiles("test")
public class DeployerServiceApiApplicationTest {
...
}
If you want to run all the tests with this profile and do not want to write this ActiveProfiles annotation you can create src/test/resources/application.properties and put into it: spring.active.profiles=test

Under what circumstances #Bean excute before #PostConstruct?

i met a urger issue, the anotain #Bean method execute before the #PostConstruct method in the same class,
in spring boot oauth2. there is a class named as
AuthorizationServerEndpointsConfiguration.java
There are 2 methods in it.
#PostConstruct
public void init() {
xxxx
}
}
#Bean
public AuthorizationServerTokenServices defaultAuthorizationServerTokenServices() {
return endpoints.getDefaultAuthorizationServerTokenServices();
}
According to my understanding,the init shuold run before defaultAuthorizationServerTokenServices method, but when i debug it in IDEA, the defaultAuthorizationServerTokenServices before init.
As below config can to reproduce this issue.
#Configuration
#Import({ ClientDetailsServiceConfiguration.class, AuthorizationServerEndpointsConfiguration.class })
class MyAuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {
.....
}
Could you tell me what's the reason?
Thanks,
We had this issue when debugging a configurer with #RefreshScope.
When we dropped the RefreshScope lazy loading it appeared to fix the issue
// #RefreshScope
public class MyAuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {
}

#Profile Spring Annotation in Camel

I have a Spring Boot + Apache Camel project that works brilliantly. I just added a new bean though where I wanted to have its implementation be profile-specific. I created Spring tests to verify it, and it works as expected, but when I run the server I get the following stack trace:
Caused by: org.apache.camel.NoSuchBeanException: No bean could be found in the registry for: MyFancyBean
at org.apache.camel.component.bean.RegistryBean.getBean(RegistryBean.java:94)
at org.apache.camel.model.language.MethodCallExpression.createExpression(MethodCallExpression.java:196)
at org.apache.camel.model.language.MethodCallExpression.createPredicate(MethodCallExpression.java:210)
at org.apache.camel.model.language.ExpressionDefinition.createPredicate(ExpressionDefinition.java:148)
at org.apache.camel.model.ValidateDefinition.createProcessor(ValidateDefinition.java:63)
at org.apache.camel.model.ValidateDefinition.createProcessor(ValidateDefinition.java:35)
at org.apache.camel.model.ProcessorDefinition.makeProcessorImpl(ProcessorDefinition.java:545)
at org.apache.camel.model.ProcessorDefinition.makeProcessor(ProcessorDefinition.java:506)
at org.apache.camel.model.ProcessorDefinition.addRoutes(ProcessorDefinition.java:222)
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1068)
I have an interface and two implementations:
public interface MyFancyBean { ... }
public class FooFancyBean implements MyFancyBean { ... }
public class NonFooFancyBean implements MyFancyBean { ... }
Depending on profile, the correct bean is read instantiated:
#Configuration
public class AppConfig {
#Bean
#Profile("foo")
MyFancyBean fooBean() {
return new FooFancyBean();
}
#Bean
#Profile("!foo")
MyFancyBean nonFooBean() {
return new NonFooFancyBean();
}
}
I've verified this works a couple of ways. First, a couple tests:
#ActiveProfiles("anything-but-foo")
#RunWith(SpringJUnit4ClassRunner.class)
#ComponentScan(basePackages = {"com.example", "com.jtv.spring.boot"})
#EnableAutoConfiguration
#Component
public class NonFooBean_SpringTest {
#Autowired
private MyFancyBean bean;
#Test
// ... here "bean" is instantiated as "NonFooFancyBean"
So the test works.
Further, when I start my app, depending on profile the correct bean in my #Configuration class above is called.
But Camel is still angry and says "NoSuchBeanException" on startup.
FWIW, here's how I'm referencing the bean:
#Component
public class MyCamelRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
// [...]
from("direct:processStuff").
validate().method("MyFancyBean").
process("MyProcessor");
}
}
How do I get Camel to honor this config?
Whoooo... Y'all get to be my rubber duck today. I just autowired it. (This doesn't work for my processor, which is why it didn't occur to me initially.)
#Component
public class MyCamelRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
// [...]
#Autowired MyFancyBean myFancyBean;
from("direct:processStuff").
validate().method(myFancyBean).
process("MyProcessor");
}
}

How to active profile as environment variable

I was configuring environment variables using spring profiles in my spring boot application. There i did configuration like
My interface is
public interface EnvConfiguration {
String getServerUrl();
}
My development configuration is
#Component
public class DevelopmentConfig implements EnvConfiguration{
#Value("${DEV}")
private String serverUrl;
#Override
public String getServerUrl(){
return serverUrl;
}
}
#Configuration
#Profile("dev")
public class DevelopmentProfile {
#Bean
public EnvConfiguration getDevelopmentConfig(){
return new DevelopmentConfig();
}
}
Same as i did configured for production environment
#Component
public class ProductionConfig implements EnvConfiguration {
#Value("${PROD}")
private String serverUrl;
#Override
public String getServerUrl(){
return serverUrl;
}
}
#Configuration
#Profile("prod")
public class ProductionProfile {
#Bean
public EnvConfiguration getProductionConfig(){
return new ProductionConfig();
}
}
Now i configured environment variable in eclipse using run configurations->agruments
-Dspring.profiles.active="dev"
Now when i trying to run my application,i am getting error:
expected single matching bean but found 2: productionConfig,developmentConfig
So please help me what am i missing there ?
Thanks in advance!
I was adding programming arguments,we have to add vm arguments
Why are you trying to configure environment properties with Java ?
You could put all your configuration into an application.properties.
Then if you want dev environment, you juste override the properties you want in application-dev.properties.
The same for prod in application-prod.properties.
Then you start as you did with -Dspring.profiles.active=dev and you will be able to retrieve value with #Value.

How to inject java.nio.file.Path dependency using #ConfigurationProperties

I'm using Spring Boot and have the following Component class:
#Component
#ConfigurationProperties(prefix="file")
public class FileManager {
private Path localDirectory;
public void setLocalDirectory(File localDirectory) {
this.localDirectory = localDirectory.toPath();
}
...
}
And the following yaml properties file:
file:
localDirectory: /var/data/test
I would like to remove the reference of java.io.File (of setLocalDirectory) by replacing with java.nio.file.Path. However, I receive a binding error when I do this. Is there way to bind the property to a Path (e.g. by using annotations)?
To add to jst's answer, the Spring Boot annotation #ConfigurationPropertiesBinding can be used for Spring Boot to recognize the converter for property binding, as mentioned in the documentation under Properties Conversion:
#Component
#ConfigurationPropertiesBinding
public class StringToPathConverter implements Converter<String, Path> {
#Override
public Path convert(String pathAsString) {
return Paths.get(pathAsString);
}
}
I don't know if there is a way with annotations, but you could add a Converter to your app. Marking it as a #Component with #ComponentScan enabled works, but you may have to play around with getting it properly registered with the ConversionService otherwise.
#Component
public class PathConverter implements Converter<String,Path>{
#Override
public Path convert(String path) {
return Paths.get(path);
}
When Spring sees you want a Path but it has a String (from your application.properties), it will lookup in its registry and find it knows how to do it.
I took up james idea and defined the converter within the spring boot configuration:
#SpringBootConfiguration
public class Configuration {
public class PathConverter implements Converter<String, Path> {
#Override
public Path convert(String path) {
return Paths.get(path);
}
}
#Bean
#ConfigurationPropertiesBinding
public PathConverter getStringToPathConverter() {
return new PathConverter();
}
}

Resources