Java Main class overriding properties file at runtime - java

I am using Apache Maven and Spring. In the src/main/resources folder I have a properties file. These property values can have different values.
I am using PropertyPlaceholderConfigurer.
#Configuration
public class ResourceConfig {
#Bean
public PropertyPlaceholderConfigurer properties( ) {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setIgnoreResourceNotFound(true);
ppc.setLocations(new ClassPathResource[] {new ClassPathResource("propertiesFile")});
return ppc;
}
}
I replace these values at runtime:
#Configuration
public class DataSourceConfig {
#Value("${jdbc.url}")
private String jdbcUrlDefault;
}
This is just a sample. I have a main method:
public static void main(String[] args) {
// accept a properties file and replace those values defined in DataSourceConfig class
}
When Apache Maven builds the application the properties file will be on the classpath. The properties file are used during the unit testing. I want to some how replace the properties with a new properties file before the main program is launched for production.
I have seen some example of Properties.load(), but I don't want to do this. I want to accept a properties file through the main program that gets replaced, so the Spring side starts the PropertyPlaceholderConfigurer.
How can this be achieved?

you can place your test properties files in src/test/resources/. In test classes, it will use properties file from this location.
properties file placed here above location will not included in your classpath in final build.
use src/main/resources to place resource files that you want in main program

Related

Passing TestPropertySource location has no affect

I am trying to pass a test.properties file to the #TestPropertySource annotation like so:
#SpringBootTest
#TestPropertySource(locations = "classpath:test.properties")
public class ApplicationTests {
#Test
public void contextLoads() {
}
#Test
public void main() {
Application.main(new String[] {});
}
}
I then call the main application, hoping that test.properties will be taken. The Application.main is a standard Boot application:
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Perhaps I have misunderstood the intended purpose of #TestPropertySource?
Thanks for any help
The following is written in the documentation:
#TestPropertySource is a class-level annotation that is used to
configure the locations() of properties files and inlined properties()
to be added to the Environment's set of PropertySources for an
ApplicationContext for integration tests.
So basically, test property sources can be used to selectively override properties
defined in system and application property source.
If #TestPropertySource is declared as an empty annotation (i.e., without explicit values for locations() or properties()), an attempt will be made to detect a default properties file relative to the class that declared the annotation. For example, if the annotated test class is com.example.MyTest, the corresponding default properties file is "classpath:com/example/MyTest.properties". If the default cannot be detected, an IllegalStateException will be thrown.
In your case, file test.properties should be placed in resources folder.
You can check this example to see how it works in action.

SpringApplicationBuilder parent webapp and child without webapp doesn't work

I have a spring boot app with a parent application context and a child application context with some legacy code. I would like to have a webapp in the parent application context, and have the child context be a non webapp.
I have my config and properties in application.yml, that gets loaded in the main class
#ComponentScan(...)
#PropertySource(value = "classpath:application.yml", factory = YamlPropertySourceFactory.class)
#SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class MyApplication(
public static void main(String[] args) {
new SpringApplicationBuilder(MyApplication.class)
.profiles("Prod")
.web(WebApplicationType.SERVLET)
.listeners(new MainAppListener()) // logging for some of the events
.child(LegacyServiceConfig.class)
.web(WebApplicationType.NONE)
.listeners(new LegacyAppListener()) // logging for some of the events
.run(args);
}
}
What I've tried so far:
I don't have any web application type config in my application.yml (spring.main.web-application-type). I hoped that the configuration I've specified thru code, in the above snippet would have worked, but it doesn't. Neither of the application contexts start as a webapp, effectively ignoring the config web(WebApplicationType.SERVLET) in the above snippet.
I tried putting spring.main.web-application-type=servlet in my
application.yaml. But that seems to apply this config to both parent
and child application context. And both of them try to start as
webapps, which is not what I want.
How do I get this to work? Any pointers?
As per my knowledge in spring-boot start from main only and the main class is provided by Maven, because I had worked on Maven project. All the properties in the project like JDBC connection, port change you can do it in .properties(extension) type file. And this file can also be in format of .yml also. These file is totally different from main class.
If you make any changes in main or add any extra annotation in main then it will throw an error.
So keep every properties in .properties or .yml file and all configuration component scan in pom.xml file you can have two .properties file also
But main class should be simple like this:
#SpringBootApplication()
public class SpringBootProjectApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootProjectApplication.class, args);
}
}
Hope this will help.

Springboot app read different path when run local vs package maven

So, I am developing an app and trying to use #ImportResource in the main class to refer to a xml file that contain configuration for SOAP services. When I am trying to run/debug in my local it's working just fine, but when I use maven package to build jar, it produce java.io.FileNotFoundException error.
Here is my main class
#SpringBootApplication
#ImportResource(locations = "jaxwsconfig.xml")
public class SoapApplication {
public static void main(String[] args) {
SpringApplication.run(SoapApplication.class, args);
}
#Bean
public ServletRegistrationBean servletRegistrationBean() {
return new ServletRegistrationBean(new WSSpringServlet(), "/Services");
}
}
Here is some errors I got when I use maven package
IOException parsing XML document from ServletContext resource [/jaxwsconfig.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/jaxwsconfig.xml]
PS: I have a question about the filepath. Where does Java Spring refer to when I don't define absolute path? Like in this case when I just define "jaxwsconfig.xml", where does it refer to?
If your jaxwsconfig.xml file is under the resource folder ie src/main/resource, you can directly give it as,
#ImportResource({"classpath*:applicationContext.xml"})
If suppose the file is present inside a folder inside resource src/main/resource/foldername. you have to give as,
#ImportResource(locations = {"classpath:foldername/application-context.xml"})

Spring junit configuration

I have tried many configurations. I try to invoke simple System.out as a test to check my configuration :
public class DaoTest {
#Test
public void commentTest() {
System.out.println("test working");
}}
My configuration classes use properties file to get the values and class that holds string constants values:
public static final String DATASOURCE_PATH = "dataSource.driver_class_name";
I annotated the test class with:
#RunWith(SpringJUnit4ClassRunner.class)
#PropertySource(
value={"classpath:spring.properties"},
ignoreResourceNotFound = true)
#ContextConfiguration(classes = { TestConfig.class, ConfigurationConstants.class, ApplicationConfigCore.class, HibernateConfig.class})
#WebAppConfiguration()
#ActiveProfiles(profiles = "test")
Where's TestConfig basically consists of classpath because I tried different ways yet still don't know where's my mistake.
Also i added the spring.properties file both to the resources file of main as well as test package.
So the question I have is where's mistake and when we deploy web app in spring it takes resources from src/main/resources folder, so what's the case with tests. Is it gonna be: src/test/resources?
In case of tests that takes the configuration, like my case, from main app configuration classes - do those classes will find right src/main/resources folder or will they try to check for test/./properties file ?
BTW. when location of properties added to #ContextConfiguration i got
Cannot process locations AND classes for context configuration exception

Spring Boot - add external property files

I have simple MVC application in SpringBoot, created using java-config (I don't have web.xml).
That application have DB connection based on JPA. Until now, all was great, but now I must move db.properties from inside of WAR to location specified by OS variable ("CONFIG_LOCATION").
In spring doc is written about that not too much. There is only say that it is posible, but how I should set that in my Spring application?
I suppose that should be done before initializer.
Then I see only two options:
- SpringApplication - there is somewhere a place where I should insert files location from OS variable but I can't find it,
- some annotation, that will understond OS variable, and add files from it to spring context before EntityManager will be created.
I'm open to suggestion how should I do that.
As mentioned in another answer #PropertySource annotation is the way to go (I'll add some details). In Java 8 you can apply it several times to your configuration class, and the order matters! For example you can make this configuration:
#SpringBootApplication
#PropertySource("classpath:/db.properties")
#PropertySource(ignoreResourceNotFound = true, value = "file:${MY_APP_HOME}/db.properties")
#PropertySource(ignoreResourceNotFound = true, value = "file:${user.home}/.myapp/db.properties")
#ComponentScan("com.myorg")
public class Application {
// ....
}
Here I assume that you should have MY_APP_HOME environment variable, and also you might want to place some settings in user home. But both configs are optional because of ignoreResourceNotFound set to true.
Also note on the order. You may have some reasonable settings for development environment in src/main/resources/db.properties. And put specific settings in host OS where your production service runs.
Look at the Resolving ${...} placeholders within #PropertySource resource locations section in javadoc for details.
If you are using the config parameters of spring-boot, it is just to specify the config location on execute jar or war, with parameter --spring.config.location.
Example:
$ java -jar myproject.jar --spring.config.location=/opt/webapps/db.properties
If you just want Spring to reference an external properties file under your project root.
Here is a simpler solution:
#Configuration
#PropertySource("file:${user.dir}/your_external_file.properties")
public class TestConfig {
#Autowired
Environment env;
}
You can change the ${user.dir} to ${user.home} if necessary.
Ok, I found a way.
I created class what return PropertySourcesPlaceholderConfigurer.
In that PSPC i get OS variable, and scan all files in that location.
After scan I was add all files with properties extension to PSCS as locations.
Method is #Bean, and class is #Configuration. After that all works fine :)
imports...
#Configuration
public class AppServiceLoader {
#Bean(name = "propLoader")
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
String mainConfigPath = StringUtils.isNotEmpty(System.getenv("CONFIG_LOCATION"))
? System.getenv("CONFIG_LOCATION") : System.getProperties().getProperty("CONFIG_LOCATION");
File configFolder = new File(mainConfigPath);
if(configFolder.isDirectory()) {
FilenameFilter ff = new FilenameFilter() {
#Override
public boolean accept(File file, String string) {
return string.endsWith(".properties");
}
};
File[] listFiles = configFolder.listFiles(ff);
Resource[] resources = new Resource[listFiles.length];
for (int i = 0; i < listFiles.length; i++) {
if(listFiles[i].isFile()) {
resources[i] = new FileSystemResource(listFiles[i]);
}
}
pspc.setLocations(resources);
}
pspc.setIgnoreUnresolvablePlaceholders(true);
return pspc;
}
}
You can also use the annotation #PropertySource. It's more clear and clean than the code solution.
See: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/PropertySource.html
For instance:
#Configuration
#PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
...

Categories