Load Config File Issue in Spring Boot - java

I am trying get values for string Constants from config.properties file in Spring boot application. But when i am trying to access those values from java classes and coming as null.
Please find the code as below:
Config.properties file:
## user Details##
user.username=xyz
user.password=123456
##customer Details ##
customer.username=abc
customer.password=123456
ConfigProp class:
#Component
#PropertySource("classpath:config.properties")
public class ConfigProp {
#Value("${user.username}")
private String userName;
#Value("${user.password}")
private String password;
#Value("${customer.username}")
private String custUserName;
#Value("${customer.password}")
private String custPassowrd;
}
Test Class:
public class Test {
public static void main(String args[]) {
ConfigProp configProp = new ConfigProp();
System.out.println(configProp.getUserName());//coming as null
}
}
SpringBoot App main class:
#SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
I am not getting the issue exactly , why values are not coming. Can anyone please help me on this and let me know in case of any other details required.

First problem here:
ConfigProp configProp = new ConfigProp();
You are not letting spring maintain this dependency and hence its not recognizing this as bean.
You Testclass can look like this:
public class Test {
#Autowired
ConfigProp configProp;
public static void main(String args[]) {
System.out.println(configProp.getUserName());//coming as null
}
}
The second problem here is presence of two main methods. You should write your Test class as Junit test which would look something like this:
#RunWith(SpringRunner.class)
#SpringBootTest
public class Test {
#Autowired
ConfigProp configProp;
#Test
public void contextLoads() {
assertEquals("abc", configProp.getCustUserName());
}
}
Also Test class should be located in src/test/java location.

Related

Spring : Cannot get value from application.properties

I created Spring project via Spring Initializr with project following struct:
I defined property in application.properties file :
my.prop=testvalue
I inject this value into MyClass as following :
#Component
class MyClass {
#Value("${my.prop}")
private String myProp;
public String getMyProp() {
return myProp;
}
}
ConfigBeans defined as following:
package com.example.propertiesdemo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class ConfigBeans {
#Bean
public MyClass myLitoBean() {
return new MyClass();
}
}
PropertiesdemoApplication.java :
package com.example.propertiesdemo;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
#SpringBootApplication
public class PropertiesdemoApplication {
public static void main(String[] args) {
ApplicationContext context
= new AnnotationConfigApplicationContext(
ConfigBeans.class);
MyClass myClass = context.getBean(MyClass.class);
System.out.println(myClass.getMyProp());
}
}
I am expecting that after executing line
System.out.println(myClass.getMyProp());
will be printed value of myprop defined in application.properties (i.e testvalue), but after running (via Netbeans IDE) I get output :
${my.prop}
What was missed / wromg in this code ? Thanks in advance
You are creating MyClass bean twice.
Using #component annotation
using #bean annotation in the config class (use method name lowerCamelCase i.e. in your case myClass())
Create bean only once using any one of the above.
You dont need to create an application context in the main method like this. The presented code is a kind of mixture of "traditional" spring and spring boot. So you're kind of bypassing all the goodies that spring boot offers, among which is automatic application.properties loading.
If you're using spring boot (there is a #SpringBootApplication annotation) then it will create everything by itself.
Usually it should be something like this
public static void main(String[] args) {
SpringApplication.run(PropertiesdemoApplication.class, args);
}
Right, as Navnath Adsul said, you need the bean to be created once, and also, since you are using Spring Boot, you need to raise the context using a special method
#SpringBootApplication
public class PropertiesdemoApplication implements CommandLineRunner {
// Inject Bean
#Autowired
private MyClass myClass;
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(PropertiesdemoApplication.class)
.run(args);
// or SpringApplication.run(PropertiesdemoApplication.class, args);
}
#Override
public void run(String[] args) throws Exception {
System.out.println(myClass.getMyProp());
}
}
#SpringBootApplication
public class PropertiesdemoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(PropertiesdemoApplication.class, args);
MyClass myClass = context.getBean(MyClass.class);
System.out.println(myClass.getMyProp());
}
}

How to load #Value annotation in the main class?

#SpringBootApplication
public class App {
#Value("${Application}")
private String application;
#Value("${APP_SERVER_CPU_ENABLED}")
private String APP_SERVER_CPU_ENABLED;
#Value("${APP_SERVER_MEMORY_ENABLED}")
private String APP_SERVER_MEMORY_ENABLED;
#Autowired
MonitoringItems mI;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
MonitoringItems mI=null;
try {
System.out.println(APP_SERVER_CPU_ENABLED);
System.out.println(APP_SERVER_MEMORY_ENABLED);
if (APP_SERVER_MEMORY_ENABLED.equalsIgnoreCase("true") && APP_SERVER_CPU_ENABLED.equalsIgnoreCase("true")) {
//do something
}
How come the main class cannot read the #Value annotations? In my other classes I have to put #Configuration, #ComponentScan, #EnableAutoConfiguration above the class and then #PostConstruct above the constructor. And I was able to retrieve the values from the application.properties. How would I be able to do that for the main class?
The problem is that you cannot make your #Values static, because Spring may not have initialized them yet. You can however get the value you want BEFORE spring is initialized by retrieving them from the system/environment variables instead of via a properties file.
String value = System.getProperty("SOME_KEY");
So after 3 years i show you an astuce to do that.
#SpringBootApplication
public class Demo1Application {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Demo1Application .class, args);
Test test = context.getBean(Test.class);
String password = test.getPassword();
System.out.println(password);
}
#Component
class Test {
#Value("${password}")
private String password;
public String getPassword() {
return password;
}
}
}
of course you create your property in a file properties like application.properties for example :
password = sxqdqdqdqdqdqd

Providing context for #Value fields from a unit test

I have a class like this:
#Service("someClient")
public class SomeClient {
#Value{some.value}
private String someValue;
public void someMethod() {
return someValue;
}
}
And a test like this:
#ContextConfiguration(locations = "classpath:/some/where/testApplicationContext.xml")
#RunWith(SpringJUnit4ClassRunner.class)
public class SomeClientTest extends TestCase {
#Value{some.value}
private String someValueTest;
#Test
public void shouldWork() {
...
someClient.someMethod()
...
}
}
When the wider application is running, the field someValue inside the SomeClient class is populated from a properties file referenced from testApplicationContext.xml. When I run the test in debug mode I can see that someValueTest is populated in the test, but when the test calls the class under test, the value is not populated.
I could use some advice! Obviously I can change the visibility of the field in the class, or provide a setter, however I would like to avoid that if possible. If it isn't, please advise.
In order to populate fields with #Value annotation in your test you need to configure PropertySourcesPlaceholderConfigurer.
Add the following to your test:
#Configuration
public static class Config {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
To read the values from test property file you can add
#TestPropertySource(locations="classpath:test.properties") to your Test class declaration
You can use ReflectionTestUtils from org.springframework.test.util.ReflectionTestUtils package to mock any variable, including the ones that access the properties file.
#RunWith(SpringJUnit4ClassRunner.class)
public class SomeClientTest extends TestCase {
private SomeClient someClient;
#Test
public void shouldWork() {
//Initialize someClient
someClient = new SomeClient();
ReflectionTestUtils.setField(someClient, "variable name", "the variable value");
someClient.someMethod()
...
}
}

How to execute a Java main class using #Configuration in a gradle project right after the beans are created

I have a config class that instantiates all the beans. I have a main class
TestUpdator() with an execute().
I plan to run this main class when I do a "gradle tomcatRunWar" to run the war which will generate the beans and run the execute() in the main class.
Example.
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "<packageName>", excludeFilters = { #Filter(type = FilterType.ANNOTATION, value = Service.class) })
#EnableSolrRepositories(basePackages = "<solrPackageName>", multicoreSupport = true)
public class JobConfig {
private static final Logger logger = LoggerFactory.getLogger(JobConfig.class);
#Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
....
}
#Bean
public SpringContextAware springContextAware() throws Exception {
.....
}
.....
//MAIN CLASS BEAN TO BE EXECUTED
#Bean(destroyMethod="shutdown")
public TestResultUpdator testResultUpdator() throws Exception {
TestResultUpdator resultUpdator = new TestResultUpdator();
/**
Bunch of dependencies for this bean
**/
resultUpdator.execute(); //call to execute()
return resultUpdator;
}
The TestUpdator class looks like this
public class TestResultUpdator {
//Variables
public static void main(String args[]) throws Exception {
//NOT SURE WHAT TO ADD HERE SINCE THE execute() will be called from the class above. But this is what I have right now commented out.
// ApplicationContext appContext = new AnnotationConfigApplicationContext(JobConfig.class);
// TestResultUpdator updator = appContext.getBean(TestResultUpdator.class);
// updator.execute();
}
public void execute() throws Exception {
logger.info("INSIDE EXECUTE FOR TEST UPDATOR!!!!");
while(true) {
//CODE TO BE EXECUTED
}
}
}
This class will execute in the background and would be executing code when the war is run.
Is there a way to do it the way I have it or is there an alternate way?
I have googled around and am not able to find good examples of this.
Basically what I am trying to do it two things,
1) when I do gradle tomcatRunWar, it will start tomcat and deploy the war
2) when it does that it should run the main class (TestUpdator) in the background by calling the execute()
Well, unless you're developing is a Spring Boot project, term main class is meaningless here.
If you're trying to execute a piece of code in TestResultUpdator class during context initialization, you can follow these steps:
1 Organize TestResultUpdator class as follows:
#Bean
public class TestResultUpdator {
#PostConstruct
public void execute() {
//CODE TO BE EXECUTED
}
}
2 Get this bean somewhere in your code:
#Autowire
private TestResultUpdator bean;

Dropwizard Integrated Testing with TestResource

Does anyone knows how to add a test Resource (i.e. one that is only for testing purposes and not added in run() method of the app)?
Here is an example:
public class MyTest {
#ClassRule
public static final DropwizardAppRule<TestConfiguration> RULE =
new DropwizardAppRule<TestConfiguration>(MyApp.class, "my-app-config.yaml");
#BeforeClass
public static void setUpBeforeClass() throws Exception
{
MyTest.RULE.getEnvironment().jersey().register(new JustForTestingResource());
}
#Test
public final void testTestResource()
{
Client client = new Client();
ClientResponse response = client.resource(
String.format("http://localhost:%d/rest/v1/test", RULE.getLocalPort()))
.get(ClientResponse.class);
assertThat(response.getStatus(), is(200));
}
}
and
public class JustForTestingRessource {
#GET
#Path("test")
#Produces(MediaType.APPLICATION_JSON)
public Response getInTestResource()
{
return Response.status(Status.OK).type(MediaType.TEXT_PLAIN).entity("get #Path(\"test\") is ok").build();
}
}
My problem is that the added resource is not added and I get resource not found 404 error response. It seems that I am registering the new resource after resource publishing and there is no refresh inside Dropwizard after start.
I dont want to extend my Application class and I dont want to insert test code into my real application code. Does anyone knows how to register the test resource without registering it in run() method of the Application?
This works, but a new class is needed:
public class TestService extends MyService{
#Override
public void run(
TestConfigurationconfiguration,
Environment environment) throws ClassNotFoundException
{
environment.jersey().register(new JustForTestingRessource());
super.run(configuration,environment);
}
}
Call in JUnit as already known:
#ClassRule
public static DropwizardAppRule<TestConfiguration> RULE =
new DropwizardAppRule<TestConfiguration>(TestService.class, "my-app-config.yaml");
Edit: Removing previous answer because it didn't solve your problem the way you wanted to do it.
I dug into the environment startup code and realized the reason why registering a controller didn't make it available is because jetty had already been started. If you stop jetty, register your controller and start jetty back up again, your resource will be available and you can use it in your test.
#BeforeClass
public static void setUpBeforeClass() throws Exception
{
MyTest.RULE.environment.applicationContext.stop()
MyTest.RULE.environment.jersey().register(new JustForTestingResource())
MyTest.RULE.environment.applicationContext.start()
}
You can test the Resource itself in a Jersey Container without starting a full dw-instance.
Check the "Testing Resources" section.
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
public class PersonResourceTest {
private static final PeopleStore dao = mock(PeopleStore.class);
#ClassRule
public static final ResourceTestRule resources = ResourceTestRule.builder()
.addResource(new PersonResource(dao))
.build();
private final Person person = new Person("blah", "blah#example.com");
#Before
public void setup() {
when(dao.fetchPerson(eq("blah"))).thenReturn(person);
// we have to reset the mock after each test because of the
// #ClassRule, or use a #Rule as mentioned below.
reset(dao);
}
#Test
public void testGetPerson() {
assertThat(resources.client().resource("/person/blah").get(Person.class))
.isEqualTo(person);
verify(dao).fetchPerson("blah");
}
}
I had the similar issue with the #ClassRule, maybe it can help to somebody..
In my test (Groovy) the invocation of RULE.getApplication() or getEnvironment() from #BeforeClass method returned null:
def setupSpec() {
RULE.application.run()
}
shown
java.lang.NullPointerException: Cannot invoke method run() on null object
I.e. RULE.testSupport had both null application and environment.
I found out that the call to
RULE.testSupport.before()
just before run() solves the error:
def setupSpec() {
RULE.testSupport.before()
RULE.application.run()
}
And then #AfterClass method:
def cleanupSpec() {
RULE.testSupport.after()
}
Or just use #Rule instead of #ClassRule and call
def setup() {
RULE.application.run()
}
inside of #Before method instead of #BeforeClass.
Though It seems strange, maybe there is some other better solution exists..
public class TestMain extends Main{
public static void main(String ... args) throws Exception {
new TestMain().run(args);
}
#Override
public void initialize(Bootstrap<AppConfiguration> bootstrap) {
super.initialize(bootstrap);
bootstrap.addBundle(
new MigrationsBundle<AppConfiguration>() {
#Override
public DataSourceFactory getDataSourceFactory(
AppConfiguration configuration) {
return configuration.getDataSourceFactory();
}
});
}
}

Categories