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.
Related
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 am using Vertx and trying to test some parameters that i am getting data from jsonfile, currently it works but i want get this file just through class path so it can be tested from a different computer.
private ConfigRetriever getConfigRetriever() {
ConfigStoreOptions fileStore = new ConfigStoreOptions().setType("file").setOptional(true)
.setConfig(new JsonObject()
.put("path", "/home/user/MyProjects/MicroserviceBoilerPlate/src/test/resources/local_file.json"));
ConfigStoreOptions sysPropsStore = new ConfigStoreOptions().setType("sys");
ConfigRetrieverOptions options = new ConfigRetrieverOptions().addStore(fileStore).addStore(sysPropsStore);
return ConfigRetriever.create(Vertx.vertx(), options);
}
My path as written above starts from /home / dir which makes it impossible to be tested on another machine. My test below uses this config
#Test
public void tourTypes() {
ConfigRetriever retriever = getConfigRetriever();
retriever.getConfig(ar -> {
if (ar.failed()) {
// Failed to retrieve the configuration
} else {
JsonObject config = ar.result();
List<String> extractedIds = YubiParserServiceCustomImplTest.getQueryParameters(config, "tourTypes");
assertEquals(asList("1", "2", "3", "6"), extractedIds);
}
});
}
I want to make the path a class path so i can test it on all environment.
I tried to access class path like this but not sure how it should be
private void fileFinder() {
Path p1 = Paths.get("/test/resources/local_file.json");
Path fileName = p1.getFileName();
}
If you have stored the file inside "src/test/resources" then you can use
InputStream confFile = getClass().getResourceAsStream("/local_file.json");
or
URL url = getClass().getResource("/local_file.json");
inside your test class (example)
IMPORTANT!
In both cases the file names can start with a / or not. If it does, it starts at the root of the classpath. If not, it starts at the package of the class on which the method is called.
Put .json file to /resources folder of your project (here an example).
Then access it via ClassLoader.getResourceAsStream:
InputStream configFile = ClassLoader.getResourceAsStream("path/to/file.json");
JsonObject config = new JsonParser().parse(configFile);
// Then provide this config to Vertx
As I understand, considering the location of your json file, you simply need to do this:
.setConfig(new JsonObject().put("path", "local_file.json"));
See this for reference.
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.
Let say I have the follow code.
private static String configFile = null;
File cf = new File(configFile);
Configuration c = new Configuration();
if (cf.exists() && cf.isFile()) {
c.configure(cf);
} else {
c.configure(configFile);
}
I am wondering what is the difference between c.configure(cf) and c.configure(configFile). In my code,configFile is repsented as resource and cf is the the configFile object.
I found these two from this (api).
public Configuration configure(String resource)
throws HibernateException
public Configuration configure(File configFile)
throws HibernateException
The documentation of the API isn't explicitly clear, is it?
I tracked it as far as this class before getting fed up:
https://github.com/hibernate/hibernate-orm/blob/master/hibernate-core/src/main/java/org/hibernate/boot/cfgxml/internal/ConfigLoader.java
But it looks like in case of configure(String resource), it is the name of a resource as would be passed to the Java class loader to get a resource as a stream, i.e.:
http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getResourceAsStream(java.lang.String)
Whereas, configure(File configFile), it uses a FileInputStream.
In either case Hibernate is still expecting the same XML format for the configuration.
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.