I have application properties file which I am dynamically updating using maven build step.
mvn clean -Dusername=user1 -Durl=xxxx -Dpassword=xxxx -DskipTests
install
jdbc.url=${url}
jdbc.username=${username}
jdbc.password=${password}
I am reading these properties in the configuration class
#Configuration
#ImportResource("classpath:/spring-beans.xml")
#PropertySource("classpath:/application.properties")
public class ApplicationConfiguration {
#Value("${jdbc.url}")
private String url;
#Value("${jdbc.username}")
private String username;
#Value("${jdbc.password}")
private String password;
#Bean(name = "c3p0DataSource")
public ComboPooledDataSource dataSource() throws PropertyVetoException,
IOException {
logger.info("Creating Datasource for {}",System.getenv("SPRING_DATASOURCE_URL"));
// logger.info("Creating Datasource for username {}",
prop.getProperty("username"));
logger.info("Creating Datasource for {}", System.getenv("username"));
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
logger.info("User Name :" + username);//returning $username instead of user1
logger.info("password :" + password);
System.out.println("User name : " + username);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource; } }
I am not getting updated values instead I am getting $username, $password as values, can anyone help me what i am missing here ?
my modified properties file look like below
jdbc.url=xxxx
jdbc.username=user1
jdbc.password=xxxx
you should run
mvn clean -Djdbc.username=user1 -Djdbc.url=xxxx -Djdbc.password=xxxx -DskipTests install
Rather than using Spring's application property , I will suggest you to use another property file, store it on file system and use org.apache.commons.configuration.PropertiesConfiguration class to load values from this file.
org.apache.commons.configuration.PropertiesConfiguration has capability to reload property file on change.
https://commons.apache.org/proper/commons-configuration/userguide/howto_properties.html
If you are using maven add below dependency.
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
When you say 'dynamically updated' it seems to me you just mean updated at build time and not at runtime. If so then you need to use the maven resources plugin, define the maven variables and use a different syntax in the properties file. This is covered in the properties and configuration section of the spring boot documentation
I have tried initialising data manually, which works. You can also give it a try.
You can try out the code below:
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Configuration;
#Configuration
#PropertySource("classpath:application.properties")
public class ApplicationConfiguration {
private Properties properties = new Properties();
public static String driverClass;
public static String dataSourceUrl;
public static String dataSourceUser;
public static String dataSourcePassword;
public ApplicationConfiguration() throws IOException {
properties.load(new InputStreamReader(ApplicationConfiguration.class.getResourceAsStream("/application.properties")));
driverClass = properties.getProperty("spring.datasource.driver-class-name");
dataSourceUrl = properties.getProperty("spring.datasource.url");
dataSourceUser = properties.getProperty("spring.datasource.username");
dataSourcePassword = properties.getProperty("spring.datasource.password");
}
// Other Code Details
}
Now I can easily use it like: ApplicationConfiguration.driverClass or ApplicationConfiguration.dataSourceUser.
Few other resources are also used by me from application.properties which I am not initialising manually and also not required while building jar. So only I am using #PropertySource("classpath:application.properties") to use other resources without initialising manually.
Try it once, It may help you :)
Related
I am learning Spring Framework and trying to inject properties from the .properties file.
This is my .properties file
sprint.datasource.username=hamnghi
sprint.datasource.password=hamnghi
sprint.datasource.url=jdbc:h2:~/test;
sprint.datasource.driver=org.h2.Driver;
When I tried to pass the driver field into the Class.forName(drive), the program could not connect to the
database and threw a java.lang.ClassNotFoundException: org/h2/Driver; but it printed the driver variable as "org.h2.Driver" to the console just fine.
My console screenshot
I also ran the program with Class.forName("org.h2.Driver"), and it ran fine; however, when I replaced it with the driver, it didn't work
This is my class.
package H2Database.db_connection;
import H2Database.functionality.Logging;
import org.springframework.beans.factory.annotation.Value;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Logger;
public class H2Connection {
private final static Logger logger = Logging.getLogger();
#Value("${sprint.datasource.url}")
private String url;
#Value("${sprint.datasource.username}")
private String username;
#Value("${sprint.datasource.password}")
private String password;
#Value("${sprint.datasource.driver}")
private String driver;
public Connection open(){
try {
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
#Override
public String toString() {
return "H2Connection{" +
"url='" + url + '\'' +
", username='" + username + '\'' +
", driver='" + driver + '\'' +
'}';
}
}
EDITED
You have trailing ; in your config. remove it
The exception means the specific class is not found in the classpath.
You need to add the dependency contains the corresponding implemenation for h2:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
You should complete the following steps to Configure the H2 database to your Spring MVC application.
Step 1: Add the following dependency in the pom.xml file
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
Step 2: configure these properties in the application.properties file.
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
Note: Spring MVC does not auto configure the properties of the
application.properties. You should configure it using configuration
class.
Step 3: Configure the Database.
#Configuration
#PropertySource("classpath:application.properties")
public class DataSourceConfig {
#Value("${sprint.datasource.url}")
private String url;
#Value("${sprint.datasource.username}")
private String username;
#Value("${sprint.datasource.password}")
private String password;
#Value("${sprint.datasource.driver}")
private String driver;
#Bean
public DataSource testDataSource() {
BasicDataSource bds = new BasicDataSource();
bds.setDriverClassName(driver);
bds.setUrl(url);
bds.setUsername(username);
bds.setPassword(password);
return bds;
}
}
I'm new to spring-boot and I'm setting up a new server.
My company separate configurations in different files (e.g.: jdbc.properties, smtp.properties, etc etc..). All configurations are put in the folder "property-files" into Tomcat folder (as sibling of "webapps" folder) and into specialized folder with name of given applicaion; for example if my application is called:"wonderful-server" all my configuration files will be in:"#TomcatFolder/property-files/wonderful-server/".
My idea is to access property files with absolute file path, like this: "file:${catalina.home}/property-files#{server.servlet.context-path}/smtp.properties".
But if I try to access "server.servlet.context-path" from a #Configuration class I obtain null.
I've tried to put into application.properties:
server.servlet.context-path=/wonderful-server
and add to my #Configuration class:
#Value("${server.servlet.context-path=/wonderful-server}") String contextPath;
but when spring bootup, contextPath contain null. The same if I use # instead of $.
Then I've tried to put into the main of my #SpringBootApplication class:
System.setProperty("server.servlet.context-path", "/formx-server");
and use in my #Configuration class:
String contextPath = System.getProperty("server.servlet.context-path");
but when spring bootup, contextPath contain null. The same if I use:
#Value("#{systemProperties['server.servlet.context-path']}") private String contextPath;
or:
#Value("#{server.servlet.context-path}") private String contextPath;
My #configuration classes are really simple, for example:
#Configuration
public class EmailConfig {
#Bean
public JavaMailSender getJavaMailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
try {
Properties props = PropertiesLoaderUtils.loadProperties( new FileSystemResource(System.getProperty("catalina.home")+"/property-files/wonderful-server/smtp.properties"));
mailSender.setHost(props.getProperty("mail.host"));
mailSender.setPort(Integer.parseInt(props.getProperty("mail.port")));
mailSender.setUsername(props.getProperty("mail.username"));
mailSender.setPassword(props.getProperty("mail.password"));
Properties properties = mailSender.getJavaMailProperties();
properties.put("mail.transport.protocol", props.getProperty("mail.transport.protocol"));
properties.put("mail.smtp.auth", props.getProperty("mail.smtp.auth"));
properties.put("mail.smtp.starttls.enable", props.getProperty("mail.smtp.starttls.enable"));
properties.put("mail.debug", props.getProperty("mail.debug"));
} catch (IOException e) {
// e.printStackTrace();
LOG.error("Error to send email: "+e.getMessage());
}
return mailSender;
}
}
In this class I've used an absolute path with static context-path, I'm trying to use it instead as variable.
In advance: thank you everyone for your precious time.
You are using =instead of :
Inside Properties:
server.servlet.context-path=/wonderful-server
But inside Configuration:
Default Value follows after ::
#Value("${server.servlet.context-path:/wonderful-server}") String contextPath;
I have a test class which I use to check if the connection to the database is established. The credentials are saved in a properties file. When I run the tests in eclipse everything works fine. But when I run a maven build the tests fail because the username used to connect to the database is not the one I set in the properties file. It is the windows username. This is my code:
Properties File:
driverClassName=oracle.jdbc.driver.OracleDriver
user=database_dev1
password=password_dev1
url=jdbc:oracle:thin:#MyAwsomeDatabase:1521:DEV01
Config Class:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories("de.xxx.bvd.mobisl.service")
#PropertySource("classpath:database.properties")
#ComponentScan("de.xxx.bvd.mobisl.service")
public class JPAConfig {
#Value("${driverClassName}")
protected String driverClassName;
#Value("${url}")
protected String url;
#Value("${user}")
protected String username;
#Value("${password}")
protected String password;
private static final Logger logger = Logger.getLogger(JPAConfig.class);
#SuppressWarnings("unchecked")
#Lazy
#Bean
public DataSource dataSource() {
try {
SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
Class<? extends Driver> driver = (Class<? extends Driver>) Class.forName(driverClassName);
dataSource.setDriverClass(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
logger.info("created DataSource with username " + username + " and password " + password);
return dataSource;
} catch (ClassNotFoundException e) {
logger.error("cannot create datasource!!", e);
return null;
}
}
As I said, running from eclipse works fine. The logfile says:
[[XXX-LOG]] 2018-09-04 08:27:23 INFO JPAConfig:57 - created DataSource with username database_dev1
[[XXX-LOG]] 2018-09-04 08:27:27 INFO JPAConfigTest:52 - got result from database
But running from maven the logfile says:
[[XXX-LOG]] 2018-09-04 08:27:53 INFO JPAConfig:57 - created DataSource with username <<Windows-Username>>
How can I tell maven to use the username from the properties file?
${user} is replaced by maven with the environment variable user.
You can get this if you run mvn help:system
Solution rename the property to be more specific like
db.username
A side effect user is very ambiguous in bigger projects. If you rename it it is more cleary where it is used
I'd like to accomplish this: Environment Specific application.properties file in Spring Boot application
in a Spring non-Boot application. Any idea on how to do that? Now I am setting environment variables to tell the application which properties to use, would prefer to do it the "boot" way.
Help would be appreciated.
In order to represent the several environments use profiles. If you want to know more browse this site. and I think this is exactly what you are looking for.
Update 1:
Considering you have a fixed suffix of your property files and you have a set of property files for different environment, for example,
development-it_wroks.properties,
test-it_wroks.properties etc. etc.
etc.it_wroks
is the suffix
Determine the active enviourment from active_env.properties
profiles.active: development
#profiles.active: test
#profiles.active: stage
#profiles.active: production
Write a custom Property resolver
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.FileBasedConfiguration;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Parameters;
public class MyPropertyUtil {
public static String getValuesFromPerpertyFile(String filename,String key){
String value = null;
Configuration config = getConfiguration(filename);
value = config.getString(key);
return value;
}
public static Configuration getConfiguration(String file){
Configuration config = null;
try{
Parameters params = new Parameters();
FileBasedConfigurationBuilder<FileBasedConfiguration>
builder =new FileBasedConfigurationBuilder
<FileBasedConfiguration>(PropertiesConfiguration.class)
.configure(params.properties().setFileName(file));
config = builder.getConfiguration();
}catch(Exception ex){
ex.printStackTrace();
}finally{
}
return config;
}
}
Now your calling class
import org.apache.log4j.Logger;
public class MyCallingClass {
final static Logger logger = Logger.getLogger(this.getClass());
//Determine the active enviourment,You may determine this from os environment variable if you want
String activeEnvironment = MyPropertyUtil.
getValuesFromPerpertyFile("resource/active_env.properties"
,"profiles.active");
//Set the property file
String myEnvSpecificValue = MyPropertyUtil.
getValuesFromPerpertyFile("resource/"+activeEnvironment+"it_wroks.properties",
"my.property.string");
//Do what you want to
logger.info(myEnvSpecificValue);
}
You can add application-environment.properties as per environment. Spring boot should automatically detect the corresponding properties file based on active environment.
I'm trying to read deployment specific information from a properties file in my wildfly configuration folder. I tried this:
#Singleton
#Startup
public class DeploymentConfiguration {
protected Properties props;
#PostConstruct
public void readConfig() {
props = new Properties();
try {
props.load(getClass().getClassLoader().getResourceAsStream("my.properties"));
} catch (IOException e) {
// ... whatever
}
}
But apparently this is not working since the configuration folder is not in the classpath anymore. Now I can't find an easy way to do it. My favorite would be something like this:
#InjectProperties("my.properties")
protected Properties props;
The only solution I found on the web so far involves making my own OSGi module, but I believe there must be an easier way to do it (one without OSGi!). Can anyone show me how?
If you want to explicitly read a file from the configuration directory (e.g. $WILDFLY_HOME/standalone/configuration or domain/configuration) there's a system property with the path in it. Simply do System.getProperty("jboss.server.config.dir"); and append your file name to that to get the file.
You wouldn't read it as a resource though, so...
String fileName = System.getProperty("jboss.server.config.dir") + "/my.properties";
try(FileInputStream fis = new FileInputStream(fileName)) {
properties.load(fis);
}
Then the file would be loaded for you.
Also, since WildFly doesn't ship with OSGi support anymore, I don't know how creating an OSGi module would help you here.
Here is a full example using just CDI, taken from this site.
Create and populate a properties file inside the WildFly configuration folder
$ echo 'docs.dir=/var/documents' >> .standalone/configuration/application.properties
Add a system property to the WildFly configuration file.
$ ./bin/jboss-cli.sh --connect
[standalone#localhost:9990 /] /system-property=application.properties:add(value=${jboss.server.config.dir}/application.properties)
This will add the following to your server configuration file (standalone.xml or domain.xml):
<system-properties>
<property name="application.properties" value="${jboss.server.config.dir}/application.properties"/>
</system-properties>
Create the singleton session bean that loads and stores the application wide properties
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
#Singleton
public class PropertyFileResolver {
private Logger logger = Logger.getLogger(PropertyFileResolver.class);
private String properties = new HashMap<>();
#PostConstruct
private void init() throws IOException {
//matches the property name as defined in the system-properties element in WildFly
String propertyFile = System.getProperty("application.properties");
File file = new File(propertyFile);
Properties properties = new Properties();
try {
properties.load(new FileInputStream(file));
} catch (IOException e) {
logger.error("Unable to load properties file", e);
}
HashMap hashMap = new HashMap<>(properties);
this.properties.putAll(hashMap);
}
public String getProperty(String key) {
return properties.get(key);
}
}
Create the CDI Qualifier. We will use this annotation on the Java variables we wish to inject into.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR })
public #interface ApplicationProperty {
// no default meaning a value is mandatory
#Nonbinding
String name();
}
Create the producer method; this generates the object to be injected
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Inject;
public class ApplicaitonPropertyProducer {
#Inject
private PropertyFileResolver fileResolver;
#Produces
#ApplicationProperty(name = "")
public String getPropertyAsString(InjectionPoint injectionPoint) {
String propertyName = injectionPoint.getAnnotated().getAnnotation(ApplicationProperty.class).name();
String value = fileResolver.getProperty(propertyName);
if (value == null || propertyName.trim().length() == 0) {
throw new IllegalArgumentException("No property found with name " + value);
}
return value;
}
#Produces
#ApplicationProperty(name="")
public Integer getPropertyAsInteger(InjectionPoint injectionPoint) {
String value = getPropertyAsString(injectionPoint);
return value == null ? null : Integer.valueOf(value);
}
}
Lastly inject the property into one of your CDI beans
import javax.ejb.Stateless;
import javax.inject.Inject;
#Stateless
public class MySimpleEJB {
#Inject
#ApplicationProperty(name = "docs.dir")
private String myProperty;
public String getProperty() {
return myProperty;
}
}
The simplest thing you can do is to run standalone.sh with a -P option referencing your properties file (you need a URL file:/path/to/my.properties, or put the file in $WILDFLY_HOME/bin).
Then all properties from the file will be loaded as system properties.
For injecting configuration properties into your application classes, have a look at DeltaSpike Configuration, which supports different property sources like system properties, environment variables, JNDI entries and hides the specific source from your application.
Alternatively, to avoid setting system properties (which will be global in the sense of being visible to all applications deployed to your WildFly instance), you can also define a custom property source for DeltaSpike reading a properties file from any given location, and these properties will be local to your application.
It sounds like the problem you are trying to solve is managing different (but probably similar) configuration files for running your application in different environments (ie, Production, QA, or even different customers). If that is the case, take a look at Jfig http://jfig.sourceforge.net/ . It would obviate the need for storing property files outside your classpath (but you still could).
What is needed is a hierarchical approach to configuration files. The ninety percent of configuration values that do not change can be maintained in a base file. The other ten percent (or less) may be maintained in their own distinct configuration file. At run time, the files are layered on top of each other to provide a flexible, manageable configuration. For example, in a development environment myhost.config.xml combines with dev.config.xml and base.config.xml to form my unique configuration.
Each configuration file may then be maintained in version control as they have unique names. Only the base files need to be modified when base values change, and it is easy to see the difference between versions. Another major benefit is that changes to the base configuration file will be exhaustively tested before deployment.
InputStream in = null;
File confDir = new File(System.getProperty("jboss.server.config.dir"));
File fileProp = new File(confDir, "my.properties");
try{
//teste fileProp.exists etc.
in = new FileInputStream(fileProp);
Properties properties = new Properties();
properties.load(in);
//You should throws or handle FileNotFoundException and IOException
}finally{
try{
in.close();
}catch(Exception ignored){
}
}
To avoid this kind of problem the issue is to set the jboss.server.config.dir in VM arguments like that :
-Djboss.server.config.dir="[jboss_repository]/server/[default-all-standard-standalone]/conf" –server
If you have in standalone.xml property:
<property name="my.properties" value="propertyValue"/>
you can wasily read it with:
static final String MY_PROPERTY = System.getProperty("my.properties");
Or if you specify context param in web.xml like:
<context-param>
<param-name>MyProperty</param-name>
<param-value>MyPropertyValue</param-value>
</context-param>
You can read it in Java bean:
String myProperty= getServletContext().getInitParameter("MyProperty");