The property file is not found when it's in a jar - spring

I've got a problem with my property file since I deploy my application within jars. When it was just in the WEB-INF/classes directory, there was no problem. My problems occur only with properties files inside jars.
Let me explain. I will simplify my code.
My application deals with buisiness objects called IPX (it's not useful to know what it is)
There's a DAO class called IpxDaoImpl with string attributes, that are SQL queries
public class IpxDaoImpl implements IpxDao extends SimpleJdbcDaoSupport {
private String listAllIpxSql; // query for listing IPX in Database
public void listAllIpx() {
// Does a query in Database using the variable listAllIpxSql
// ...
}
public void setListAllIpxSql(String listAllIpxSql) {
this.listAllIpxSql = listAllIpxSql;
}
}
This class is defined in a config xml file, called appContext-dao.xml. It contains :
<bean id="ipxDao" class="com.IpxDaoImpl" parent="myGenericDao">
<property name="listAllIpxSql" value="${ipx.list}" />
</bean>
There is another config xml file, that tells how to load the properties files : appContext-commo-dao.xml. It contains :
<context:property-placeholder location="classpath*:**/sql.properties" />
And there is a sql.properties file proper to queries about IPX. It contains :
ipx.list=SELECT * FROM IPX
Now, when I deploy my application in WEB-INF/classes, everything is fine. But we have an integration environement where we have to generate jars. All the files I've mentioned above are in the same jar. And then, when I deploy, I've got this error :
Could not resolve placeholder 'ipx.list'
It seems that the property file sql.properties is not found.
I've tried to extract this property file and put it in the WEB-INF/classes directory, with the proper folder. And now it works.
So, what is wrong with my code? Is it the placeholer ?
Thanks in advance.
Cedric

Try with classpath:/sql.properties
From reference:
Please note that "classpath*:" when combined with Ant-style patterns
will only work reliably with at least one root directory before the
pattern starts, unless the actual target files reside in the file
system.
See http://docs.spring.io/spring/docs/2.5.5/reference/resources.html#resources-wildcards-in-path-other-stuff for more info.

Related

Load Yml at runtime with Spring Boot

I have multiple yml files in different folders. All the files in the folder share the same property structure which I mapped with a java bean.
At runtime, with a factory, I want to get the right bean populated with the values of the specific file chosen at runtime. How do I do that?
Thanks
The #ConfigurationProperties annotation or the mechanism behind it is built to be used for configuration of an application at startup, not loading data at runtime.
I'm sure you could somehow start mini spring environments at runtime just to read this data using different spring profiles (this is e.g. how spring-cloud-configserver loads properties) but this seems not right and there are better alternatives.
E.g., if you need that data to be loaded at runtime, you can use jackson's yamlfactory for that, with that you can read your data in 3-4 statements. A good example is here: https://www.baeldung.com/jackson-yaml.
Consider a Bean like this: (Pseudo code, just to explain)
class MyConfigBean {
private Properties currentProperties;
private Map<String, Properties> allPropertiesMap;
void loadAllProperties() { ... }
void switchProperties(String name) {
this.currentProperties = this.allPropertiesMap.get(name);
}
String getProperty(String key) {
return this.currentProperties.get(key);
}
}
You can load all of the Yaml files into a Map in your bean. The Map's key could be the "name" of the properties file and the value would be the Properties object.
A switchProperties(String name) method will "select" the properties file you wish to work with. Using the name, you will get the appropriate Properties object from the Map and assign it to the "currentProperties" object.
This way, each time you get a property by key, it will be fetched from the "currentProperties" according to what you "switched" for.
Important - You'll have to decide what is the default properties after you load all of them.

How does Spring know which property file to refer to for getting the value of the variable annotated with #Value?

What if there are multiple property files in our application and both of those files have that variable with different values set?
We usually just inject the value as below and it somehow always manages to get the value form properties file. How?
#Configuration
public class AppConfig {
#Value("${spring.datasource.url}")
private String datasourceUrl;
Spring Boot has many possible sources of configuration.
When it comes to property files it checks for application.properties and then application-<active_profile>.properties where <active_profile> is set by spring.profiles.active environment variable (the same holds for *.yaml files).
It will search for property files applying the above rule in the following directories in this precedence:
(higher on the list overrides properties loaded from the lower locations)
/config subdirectory of the current directory
current ./ directory
classpath's /config package (everything in src/main/resources/config if you use maven)
classpath's root / (everything in src/main/resources if you use maven)
The value from the last file that Spring reads will overwrite all previously read values. If you define the order in which files are read yourself (via configuration for example) than you have full control over it. Have a look at the follwing examples:
Annotation based config:
#Configuration
#PropertySource({"classpath:foo.properties", "classpath:bar.properties"})
public class PropertiesWithJavaConfig {
//...
}
XML-based config:
<context:property-placeholder location="classpath:foo.properties,classpath:bar.properties"/>
If bar.properties contains properties which are also defined in foo.properties, the value from bar.properties overwrites the one from foo.properties.

Neo4j/SDN warining: No identity field found for class of type for exception class

In my Neo4j/Spring Data Neo4j project I have a following exception class:
public class CriterionNotFoundException extends NotFoundDomainException {
private static final long serialVersionUID = -2226285877530156902L;
public CriterionNotFoundException(String message) {
super(message);
}
}
During application startup I see a following WARN:
WARN o.s.d.n.m.Neo4jPersistentProperty - No identity field found for class of type: com.example.domain.dao.decision.exception.DecisionAlreadyExistsException when creating persistent property for field: null
Why Neo4j/SDN is looking for identity field in this class ? How to correctly configure my application in order to skip this warning ?
You can ignore this warning- this is produced by SDN when building metadata Spring Data REST integration. It should not be doing this for Exceptions of course, and we'll have this fixed.
One way "to correctly configure [your] application" would be add EnableNeo4jRepositories and EntityScan annotations to your SpringBootApplication (or your config bean) as mentioned here and specify the names of your packages with Neo4J relevant classes.
I've debugged the SDN/Neo4j code only for 5 minutes, so my guesses may be off, but, I believe those warnings are generated when you don't specify packages to scan for your entities, and repositories. I'm guessing in that case SpringBoot+Neo4J-mapping scans each and every class in your project, and if a class has some fields, but nothing resembling an "id" field, it spits this warning. (So adding a Long id field to the classes with warnings may be another (yes, very ugly) work-around as well)
I've seen those warnings vanished when I tried explicitly specifying package names in my project using SpringBoot 2.0.6 + spring-data-neo4j 5.0.11.

Obtaining configured properties from Springs

I am trying to obtain properties' values configured in Springs context with Environment bean (like in spring PropertyPlaceholderConfigurer and context:property-placeholder checked answer).
public class SpringsPropertiesProvider implements IPropertiesProvider {
#Autowired Environment envinronment;
#Override
public String getProperty(String key) {
return envinronment.getProperty(key);
}
}
This class is registered with following xml:
<context:property-placeholder
location="classpath:myproject/example.properties" />
<context:annotation-config />
<bean class="myproject.SpringsPropertiesProvider" id="springsPropertiesProvider"/>
But SpringsPropertiesProvider.getProperty method does not return values configured within example.properties file.
What I am doing wrong and how can I get dynamic access to properties configured by placeholderconfigurer?
PS.
During environment.getPropert(key) call debugging shows that org.springframework.core.env.PropertySourcesPropertyResolver has only two entries in its propertySources field ([systemProperties,systemEnvironment]) and both entries does not contain any keys defined within example.properties.
Try this
<context:property-placeholder
location="classpath:myproject/example.properties" ignore-resource-not-found="true"/>
If the project does not startup then that means spring was not able to locate the properties file.
Speaking of which, what does your project structure look like?
Update:
The following link explains why this is not working

reading a dynamic property list into a spring managed bean

I've been searching but cannot find these steps. I hope I'm missing something obvious.
I have a properties file with the following contents:
machines=A,B
I have another file like that but having a different number of members in the machines element like this:
machines=B,C,D
My question is how do I load this variable-length machines variable into a bean in my spring config in a generic way?
something like this:
<property name="machines" value="${machines}"/>
where machines is an array or list in my java code. I can define it however I want if I can figure out how to do this.
Basically I'd rather have spring do the parsing and stick each value into a list element instead of me having to write something that reads in the full machines string and do the parsing myself (with the comma delimiter) to put each value into an array or list. Is there an easy way to do this that I'm missing?
You may want to take a look at Spring's StringUtils class. It has a number of useful methods to convert a comma separated list to a Set or a String array. You can use any of these utility methods, using Spring's factory-method framework, to inject a parsed value into your bean. Here is an example:
<property name="machines">
<bean class="org.springframework.util.StringUtils" factory-method="commaDelimitedListToSet">
<constructor-arg type="java.lang.String" value="${machines}"/>
</bean>
</property>
In this example, the value for 'machines' is loaded from the properties file.
If an existing utility method does not meet your needs, it is pretty straightforward to create your own. This technique allows you to execute any static utility method.
Spring EL makes easier.
Java:
List <String> machines;
Context:
<property name="machines" value="#{T(java.util.Arrays).asList('${machines}')}"/>
If you make the property "machines" a String array, then spring will do it automatically for you
machines=B,C,D
<property name="machines" value="${machines}"/>
public void setMachines(String[] test) {
Since Spring 3.0, it is also possible to read in the list of values using the #Value annotation.
Property file:
machines=B,C,D
Java code:
import org.springframework.beans.factory.annotation.Value;
#Value("#{'${machines}'.split(',')}")
private List<String> machines;
You can inject the values to the list directly without boilerplate code.(Spring 3.1+)
#Value("${machines}")
private List<String> machines;
for the key "machines=B,C,D" in the properties file by creating following two instance in your configuration.
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public ConversionService conversionService() {
return new DefaultConversionService();
}
Those will cover all the separate based split and whitespace trim as well.

Resources