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;
Related
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 :)
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.
In my application.properties I add some custom attributes.
custom.mail.property.subject-message=This is a ä ö ü ß problem
In this class I have the representation of the custom attributes.
#Component
#ConfigurationProperties(prefix="custom.mail.property")
public class MailProperties {
private String subjectMessage;
public String getSubjectMessage() {
return subjectMessage;
}
public void setSubjectMessage(String subjectMessage) {
this.subjectMessage = subjectMessage;
}
And here I use my MailProperties:
#Service
public class SimpleUnknownResponseMessage extends MailProperties implements UnknownResponseMessage{
private JavaMailSender javaMailSender;
#Autowired
public SimpleUnknownResponseMessage(JavaMailSender javaMailSender) {
this.javaMailSender = javaMailSender;
}
#Override
public void placeUnknownResponse(BookResponse bookResponse) {
MimeMessage message = javaMailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, "UTF-8");
helper.setSubject(this.getSubjectMessage());
javaMailSender.send(message);
} catch (MessagingException e) {
e.printStackTrace();
}
}
While debugging I can see that my this.getSubjectMessage() variable has this value inside: This is a ä ö ü à problem. So before sending my mail I already have an UTF-8 encoding problem.
I already checked the encoding of the application.properties file and its UTF-8.
My IDE(STS/Eclipse) and the project properties are also set on UTF-8.
How can I set the UTF-8 encoding for the text of my custom attributes in the application.properties file?
As already mentioned in the comments .properties files are expected to be encoded in ISO 8859-1. One can use unicode escapes to specify other characters. There is also a tool available to do the conversion. This can for instance be used in the automatic build so that you still can use your favorite encoding in the source.
Please, try to add PropertySource annotation with encoding parameter into your Configuaration file:
#PropertySource(value = "classpath:application-${env}.properties", encoding = "UTF-8")
Hope it helps.
I've faced with the same problem.
In Spring Boot there are 2 PropertySourceLoader which are used to load properties in application:
PropertiesPropertySourceLoader - supports UTF-8 only when load from XML
YamlPropertySourceLoader - supports UTF-8, but you have to change configuration format to use it
They're listed in the file https://github.com/spring-projects/spring-boot/blob/master/spring-boot/src/main/resources/META-INF/spring.factories
So we decided to write our own implementation of PropertySourceLoader which would be able to load properties from UTF-8 file correctly. The idea is from answer #BalusC - How to use UTF-8 in resource properties with ResourceBundle
Our PropertySourceLoader implementation:
public class UnicodePropertiesPropertySourceLoader implements PropertySourceLoader {
#Override
public String[] getFileExtensions() {
return new String[]{"properties"};
}
#Override
public PropertySource<?> load(String name, Resource resource, String profile) throws IOException {
if (profile == null) {
Properties properties = new Properties();
PropertyResourceBundle bundle = new PropertyResourceBundle(new InputStreamReader(resource.getInputStream(), "UTF-8"));
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
properties.setProperty(key, bundle.getString(key));
}
if (!properties.isEmpty()) {
return new PropertiesPropertySource(name, properties);
}
}
return null;
}
}
Then we created file resources/META-INF/spring.factories with content:
# Custom PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
your.own.package.UnicodePropertiesPropertySourceLoader
Now we have 3 PropertySourceLoader in our application in following order:
UnicodePropertiesPropertySourceLoader
PropertiesPropertySourceLoader
YamlPropertySourceLoader
NOTES!
I'm not sure that it is proper usage of PropertyResourceBundle
I'm not sure that order of PropertySourceLoaders in Spring Boot will be the same if you make a dedicated library to reuse it in other projects.
In our project this solution works fine.
UPDATE!
It's better to implement load method of UnicodePropertiesPropertySourceLoader without PropertyResourceBundle:
#Override
public PropertySource<?> load(String name, Resource resource, String profile) throws IOException {
if (profile == null) {
Properties properties = new Properties();
properties.load(new InputStreamReader(resource.getInputStream(), "UTF-8"));
if (!properties.isEmpty()) {
return new PropertiesPropertySource(name, properties);
}
}
return null;
}
just converted the text with the special chars with https://native2ascii.net/
To set the UTF-8 encoding for the text in the application.properties (and any other Java properties as well as environment variables) add -Dfile.encoding=UTF-8 to java command line agrs.
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");
In my current spring project, when I run the application, it's created a directory on the user's home directory where I store some configuration files (*.properties file). In my code, I refer to this file in that way:
private String getFilename() {
return System.getProperty("user.home")+File.separator+".webapp"+File.separator+"webapp.preferences";
}
which allows me run the application in any operational system without change the code. I need add this directory to the classpath of the application, to allow me use the annotation PropertySource to access the properties stored in the file using either the method getproperty from Enviroment class or the Value annotation.
I using spring-boot, so the start point for the application it's that:
#Controller
#EnableJpaRepositories
#EnableAutoConfiguration
#ComponentScan(value="com.spring")
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
I also have classes WebAppInitializer, WebAppConfig and DispatcherConfig to store the configurations handled by the XML files web.xml and dispatcher-config.xml from spring.
Anyone can tell if this is possible and hor to accomplish that?
UPDATE
Following the sugestions in the commentaries, I add to my project this bean:
#Bean
static PropertySourcesPlaceholderConfigurer property() throws Exception {
PropertySourcesPlaceholderConfigurer propertyConfigurer = new PropertySourcesPlaceholderConfigurer();
String filename = System.getProperty("user.home")+File.separator+".webapp"+File.separator+"webapp.preferences";
File file = new File( filename );
if(file.exists())
propertyConfigurer.setLocation( new FileSystemResource( filename ) );
else {
if(file.mkdir()) {
FileOutputStream fos = new FileOutputStream( filename );
fos.close();
propertyConfigurer.setLocation( new FileSystemResource( filename ) );
}
}
return propertyConfigurer;
}
and try use this in my pojo class:
#Input(label = "Titulo")
#Property(key = "geral.titulo")
#Value(value = "${geral.titulo}")
private String titulo;
but when I create a new instance of this classe, the fields don't receive the value indicated by the annotation. What I am doing wrong? I verify the file and the properties exists in it.