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.
Related
I am using SnakeYAML as my YAML parser for a project, and I don't know how to set keys that are nested. For instance, here is a YAML file with nested keys in it.
control:
advertising:
enabled: true
logging:
chat: true
commands: true
format: '%id% %date% %username% | %value%'
My goal is to be able to easily set the path control.advertising.enabled or any other path to any value.
When I use
void Set(String key, Object value, String configName){
Yaml yaml = new Yaml();
OutputStream oS;
try {
oS = new FileOutputStream(main.getDataFolder() + File.separator + configName);
} catch (FileNotFoundException e) {
e.printStackTrace();
return;
}
Map<String, Object> data = new HashMap<String, Object>();
// set data based on original + modified
data.put(key, value);
String output = yaml.dump(data);
try {
oS.write(output.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
to set the value, instead of getting
logging:
chat: true
commands: true
format: '%id% %date% %username% | %value%'
the entire yaml file is cleared and I only get
{logging.chat: false}
Thank you!
Define your structure with Java classes:
public class Config {
public static class Advertising {
public boolean enabled;
}
public static class Control {
public Advertising advertising;
}
public static class Logging {
public boolean chat;
public boolean commands;
public String format;
}
Control control;
Logging logging;
}
Then you can modify it like this:
Yaml yaml = new Yaml();
Config config = yaml.loadAs(inputStream, Config.class);
config.control.advertising.enabled = false;
String output = yaml.dump(config);
Note that loading & saving YAML data this way might mess with the order of mapping keys, because this order is not preserved. I assume that the output order will be according to the field order in the Java classes, but I'm not sure.
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;
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.
I'm trying to write a test for a Mule flow that will involve dropping a file in a location, waiting for it to be processed by my flow and compare the output to see if it has been transformed correctly. My flow looks as follows:
<flow name="mainFlow" processingStrategy="synchronous">
<file:inbound-endpoint name="fileIn" path="${inboundPath}">
<file:filename-regex-filter pattern="myFile.csv" caseSensitive="true"/>
</file:inbound-endpoint>
...
<file:outbound-endpoint path="${outboundPath}" outputPattern="out.csv"/>
</flow>
Is there a way I can access the inboundPath and outboundPath Mule properties inside of my test class so that I can drop files and wait for output in the correct places?
The test class I'm using is:
public class MappingTest extends BaseFileToFileFunctionalTest {
#Override
protected String getConfigResources() {
return "mappingtest.xml";
}
#Test
public void testMapping() throws Exception {
dropInputFileIntoPlace("myFile.csv");
waitForOutputFile("out.csv", 100);
assertEquals(getExpectedOutputFile("expected-out.csv"), getActualOutputFile("out.csv"));
}
}
Which extends this class:
public abstract class BaseFileToFileFunctionalTest extends FunctionalTestCase {
private static final File INPUT_DIR = new File("/tmp/muletest/input");
private static final File OUTPUT_DIR = new File("/tmp/muletest/output");
private static final Charset CHARSET = Charsets.UTF_8;
#Before
public void setup() {
new File("/tmp/muletest/input").mkdirs();
new File("/tmp/muletest/output").mkdirs();
empty(INPUT_DIR);
empty(OUTPUT_DIR);
}
private void empty(File inputDir) {
for (File file : inputDir.listFiles()) {
file.delete();
}
}
protected File waitForOutputFile(String expectedFileName, int retryAttempts) throws InterruptedException {
boolean polling = true;
int attemptsRemaining = retryAttempts;
File outputFile = new File(OUTPUT_DIR, expectedFileName);
while (polling) {
Thread.sleep(100L);
if (outputFile.exists()) {
polling = false;
}
if (attemptsRemaining == 0) {
VisibleAssertions.fail("Output file did not appear within expected time");
}
attemptsRemaining--;
}
outputFile.deleteOnExit();
return outputFile;
}
protected void dropInputFileIntoPlace(String inputFileResourceName) throws IOException {
File inputFile = new File(INPUT_DIR, inputFileResourceName);
Files.copy(Resources.newInputStreamSupplier(Resources.getResource(inputFileResourceName)), inputFile);
inputFile.deleteOnExit();
}
protected String getActualOutputFile(String outputFileName) throws IOException {
File outputFile = new File(OUTPUT_DIR, outputFileName);
return Files.toString(outputFile, CHARSET);
}
protected String getExpectedOutputFile(String resourceName) throws IOException {
return Resources.toString(Resources.getResource(resourceName), CHARSET);
}
}
As you can see I'm currently creating temporary input/output directories. I'd like to make this part read from the Mule properties if possible? Thanks in advance.
After observing your test classes and code I could see that you want to dynamically create temp folders place files in them. And the flow should read the files from Temp Directory and write output to another Temp directory. Point to be noted is that Mule's Endpoints are created when the configuration is loaded. So the ${inbound} and ${outbound} should be provided to the mule flow by the time they are provided.
So one option can be to create a dummy flow pointing to the temp folders for testing.
or
Create a test properties file pointing to the temp folders and load that to your flow config, so that your flow endpoints will get the temp folder paths.
In any way path cannot be provided to the flow inbound endpoints after they have been created(on config load).
Update1:
As per your comment the solution with option would be like the following.
Seperate the properties loading part of the config into another config.
Like "mapping-core-config.xml,mappingtest.xml" where the mapping-core-config will have the tags to load the properties file.
Now create a test config file for the mapping-core-config.xml file which loads the test properties file. This should be used in your test config. This way without modifying or disturbing your main code, you can test your flows pointing to temp folders.
"mapping-core-test-config.xml,mappingtest.xml"
Note: The test config can reside in the src/test/resources folders.
Hope this helps.
How to switch from ResourceBundle to Properties (class)?
I have an app split into 2 Java projects (core & web). A Java service in the core module have to read values from a .properties file located in the web module.
When I use ResourceBundle, it works as expected.
I wanted to switch to the Properties class for several reasons (esp. because the ResourceBundle is cached and I don't want to implement the ResourceBundle.Control to have no cache).
Unfortunately I can't get it to work, particularly because I can't find out which correct relative path to use.
I read the decompiled ResourceBundle class (et al.) and noticed the use of getResource() on some ClassLoader.
So instead of directly using FileInputStream, I tested with getResource() or simply getResourceAsStream() on ServiceImpl.class or ResourceBundle.class but still no success...
Anyone having an idea how to get this work? Thanks!
This is my app core with the service getting the property values:
app-core
src/main/java
com.my.company.impl.ServiceImpl
public void someRun() {
String myProperty = null;
myProperty = getPropertyRB("foo.bar.key"); // I get what I want
myProperty = getPropertyP("foo.bar.key"); // not here...
}
private String getPropertyRB(String key) {
ResourceBundle bundle = ResourceBundle.getBundle("properties/app-info");
String property = null;
try {
property = bundle.getString(key);
} catch (MissingResourceException mre) {
// ...
}
return property;
}
private String getPropertyP(String key) {
Properties properties = new Properties();
InputStream inputStream = new FileInputStream("properties/app-info.properties"); // Seems like the path isn't the good one
properties.load(inputStream);
// ... didn't include all the try/catch stuff
return properties.getProperty(key);
}
This is the web module where resides the properties file:
app-web
src/main/resources
/properties
app-info.properties
You should use getResource() or getResourceAsStream() with proper path and classloader.
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("properties/app-info.properties");
Make sure the file is named app-info.properties, and not something like app-info_en.properties which would be found by ResourceBundle (when the context matches) but not by getResourceAsStream().
You should not be trying to read the properties from the filesystem. Change your method that gets properties to load them from a resource stream instead. Pseudo code:
private String getPropertyP(final String key) {
final Properties properties = new Properties();
final InputStream inputStream = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("properties/app-info.properties");
properties.load(inputStream);
return properties.getProperty(key);
}