I have a properties file which I would like loaded in to System Properties so that I can access it via System.getProperty("myProp"). Currently, I'm trying to use the Spring <context:propert-placeholder/> like so:
<context:property-placeholder location="/WEB-INF/properties/webServerProperties.properties" />
However, when I try to access my properties via System.getProperty("myProp") I'm getting null. My properties file looks like this:
myProp=hello world
How could I achieve this? I'm pretty sure I could set a runtime argument, however I'd like to avoid this.
Thanks!
In Spring 3 you can load system properties this way:
<bean id="systemPropertiesLoader"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" value="#{#systemProperties}" />
<property name="targetMethod" value="putAll" />
<property name="arguments">
<util:properties location="file:///${user.home}/mySystemEnv.properties" />
</property>
</bean>
While I subscribe to the Spirit of Bozho's answer, I recently also had a situation where I needed to set System Properties from Spring. Here's the class I came up with:
Java Code:
public class SystemPropertiesReader{
private Collection<Resource> resources;
public void setResources(final Collection<Resource> resources){
this.resources = resources;
}
public void setResource(final Resource resource){
resources = Collections.singleton(resource);
}
#PostConstruct
public void applyProperties() throws Exception{
final Properties systemProperties = System.getProperties();
for(final Resource resource : resources){
final InputStream inputStream = resource.getInputStream();
try{
systemProperties.load(inputStream);
} finally{
// Guava
Closeables.closeQuietly(inputStream);
}
}
}
}
Spring Config:
<bean class="x.y.SystemPropertiesReader">
<!-- either a single .properties file -->
<property name="resource" value="classpath:dummy.properties" />
<!-- or a collection of .properties file -->
<property name="resources" value="classpath*:many.properties" />
<!-- but not both -->
</bean>
The point is to do this the other way around - i.e. use system properties in spring, rather than spring properties in the system.
With PropertyPlaceholderConfigurer you get your properties + the system properties accessible via the ${property.key} syntax. In spring 3.0 you can inject these using the #Value annotation.
The idea is not to rely on calls to System.getProperty(..), but to instead inject your property values. So:
#Value("${foo.property}")
private String foo;
public void someMethod {
String path = getPath(foo);
//.. etc
}
rather than
public void someMethod {
String path = getPath(System.getProperty("your.property"));
//.. etc
}
Imagine you want to unit test your class - you'd have to prepopulate the System object with properties. With the spring-way you'd just have to set some fields of the object.
#Configuration
Public class YourAppConfig {
#Value("${your.system.property}") String prop
#Autowired
Public void LoadProperties() {
System.setProperty(PROPERTY_NAME, prop) ;
}
#Bean
...
Related
I have an xml bean configuration as follows:
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource" />
<property name="jpaDialect" ref="openJPADialect" />
</bean>
I want to add a new property defaultTimeout, however I don't want to hard code it.
I want to instead put some class that will retrieve the value of this property from some in memory cache (doesn't matter from where actually)
I've heard and used before - org.springframework.beans.factory.config.PropertyPlaceholderConfigurer However it retrieves values from a property file, which is not exactly what I need.
Could you please advise my direction?
I want to put instead of this property some class that will retrieve value of this property from some in memory cache (doesn't matter from where actually)
How about injecting your txManager into this some class and set the defaultTimeout there?
Try looking into the com.typesafe.config library https://www.javadoc.io/doc/com.typesafe/config/1.2.1. This allows you to load configuration files.
Use this library to create a bean of type config. Something like this. This is a java configuration, but could be adapted to an XML implementation.
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.your.package")
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Bean
public Config properties() throws Exception {
String path = ""; // path to properties file
Config conf = ConfigFactory.parseFile(new File(path));
return conf;
}
}
Then in your component classes, you can autowire the bean and use the properties stored in the in memory bean.
#Autowired
private Config properties;
...
properties.getString("your property key");
I need to play around keys of a property file. Key will be dynamic, so I need the bean of property file as mentioned below as my current running Spring application.
Spring Configuration:
<bean id="multipleWriterLocations" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:writerLocations.properties</value>
<value>file:config/writerLocations.properties</value>
</list>
</property>
</bean>
Java Code:
Properties prop = appContext.getBean("multipleWriterLocations")
I need the same Properties bean instance in Spring-boot. I need to transform existing Spring application into Spring-Boot without changing the functionalities.
One way to get properties file value using #PropertySource(), but in this case I need the key name. But in my case, key name is not known and I need to fetch keySet from the Properties bean.
You can use #ImportResource("classpath:config.xml") where config.xml contains your PropertiesFactoryBean above, and then autowire it into your #SpringBootApplication or #Configuration class.
#SpringBootApplication
#ImportResource("classpath:config.xml")
public class App {
public App(PropertiesFactoryBean multipleWriterLocations) throws IOException {
// Access the Properties populated from writerLocations.properties
Properties properties = multipleWriterLocations.getObject();
System.out.println(properties);
}
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
I have an xml configurable Spring context with the following property placeholders:
<context:property-placeholder
properties-ref="dbProperties" location="classpath:logmessages.properties" order="2"/>
<context:property-placeholder location="classpath:application.properties" order="1"/>
DB properties configuration bean is as follows:
<bean id="dbProperties"
class="com.example.DatabasePropertiesLoader">
<property name="path" value="${db.path}"/>
</bean>
As it comes from the name, this bean loads some properties from the database, for example endpoints and credentials to other services. However, to access the database that keeps this properties it also needs credentials, which are kept in application.properties:
public class DatabasePropertiesLoader extends AbstractFactoryBean<Properties> {
private String path;
#Override
protected Properties createInstance() throws Exception {
// logic loading properties
}
#Override
public Class<Properties> getObjectType() {
return Properties.class;
}
}
Path property kept in application.properties file:
db.path=localhost:7777
As you see, this bean requires "path" property to be injected to be created.
However, it can't be done, because injected value is null. I guess that Spring only knows about application.properties file, not about its contents. Is there any way to solve this?
You need to tell spring to use the properties as below.
#Value("${db.path}")
private String path;
I am rewriting a legacy project using Quartz and Spring Framework. The original configuration is in XML and now I am translating it into Java Config. The xml configuration uses jobDetail to set the job detail property of the trigger bean. However, when I try to use the equivalent method, i.e. the setter: setJobDetails(simpleJobDetail), I got a warning that the setter does not have correct type (expecting JobDetails, but got MethodInvokingJobDetailFactoryBean).
May I know whether it is correct to translate xml bean configuration to Java COnfig by using the equivalent named setter in Java COnfig?
Why in the XML property setting, the trigger bean can set its jobDetail property as the simpleJobDetail bean (which has type MethodInvokingJobDetailFactoryBean) , while the Java Config could not?
XML config:
<!-- For times when you just need to invoke a method on a specific object -->
<bean id="simpleJobDetail"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="reader" />
<property name="targetMethod" value="readData" />
</bean>
<!-- Run the job every 60 seconds with initial delay of 1 second -->
<bean id="trigger"
class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="simpleJobDetail" />
<property name="repeatInterval" value="600000" />
</bean>
Java Config:
#Bean
public MethodInvokingJobDetailFactoryBean simpleJobDetail() {
MethodInvokingJobDetailFactoryBean simpleJobDetail = new MethodInvokingJobDetailFactoryBean();
simpleJobDetail.setTargetObject(reader());
simpleJobDetail.setTargetMethod("readData");
return simpleJobDetail;
}
#Bean
private Object reader() {
// TODO: 10/13/2016
return null;
}
#Bean
public SimpleTriggerFactoryBean trigger() {
final SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean();
trigger.setJobDetail( simpleJobDetail()); // got warning about wrong type
trigger.setRepeatInterval(60000);
return trigger;
}
Note that simpleJobDetail() returns a factory, not the bean itself. You can rely on autowiring to inject a JobDetail built using this factory.
#Bean
public SimpleTriggerFactoryBean trigger(final JobDetail simpleJobDetail) {
final SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean();
trigger.setJobDetail(simpleJobDetail); // got warning about wrong type
trigger.setRepeatInterval(60000);
return trigger;
}
Hope it helps.
I have a spring context xml file with this
<context:property-placeholder location="classpath:cacheConfig.properties"/>
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="cacheManagerName" value="cacheName"/>
<property name="shared" value="false"/>
<property name="configLocation" value="classpath:cacheConfig.xml"/>
</bean>
the goal is to allow the customer to edit the properties file, like this
cache.maxMemoryElements="2000"
and then in the actual cacheConfig.xml file have this
<cache name="someCacheName"
maxElementsInMemory="${cache.maxMemoryElements}" ... />
so that items we do not want the customer to change are not exposed. Of course the above details are only partially detailed and NOT working. Currently I see this in the log file
Invocation of init method failed; nested exception is net.sf.ehcache.CacheException: Error configuring from input stream. Initial cause was null:149: Could not set attribute "maxElementsInMemory".
Thanks in advance...
Your example uses EhCacheManagerFactoryBean to expose a reference to the CacheManager, with caches defined in the external cacheConfig.xml file. As #ChssPly76 pointed out, Spring's property resolver only works within Spring's own bean definition files.
However, you don't have to define the individual caches in the external file, you can define them right within the Spring bean definition file, using EhCacheFactoryBean:
FactoryBean that creates a named
EHCache Cache instance... If the
specified named cache is not
configured in the cache configuration
descriptor, this FactoryBean will
construct an instance of a Cache with
the provided name and the specified
cache properties and add it to the
CacheManager for later retrieval.
In other words, if you use EhCacheFactoryBean to refer to a named cache that isn't already defined in cacheConfig.xml, then Spring will create and configure a new cache instance and register it with the CacheManager at runtime. That includes specifying things like maxElementsInMemory, and because this would be specified in the Spring bean definition file, you get full support of the property resolver:
<context:property-placeholder location="classpath:cacheConfig.properties"/>
<bean id="myCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager"/>
<property name="maxElementsInMemory" value="${cache.maxMemoryElements}"/>
</bean>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="shared" value="false"/>
<property name="configLocation" value="classpath:cacheConfig.xml"/>
</bean>
This is not how PropertyPlaceholderConfigurer works. It can be used to replace values within context, but not within arbitrary external files. And cacheConfig.xml is an external file - it's just being passed by Spring to EH Cache.
If you are using Maven or Ant, both offer the ability to do filtering of tokens in resource files.
For Maven, you could do something like
<cache name="someCacheName"
maxElementsInMemory="${cache.maxMemoryElements}" ... />
And in a filter file, or in the POM itself, have
cache.maxMemoryElements = 200
Resource Filtering in Maven: The Definitive Guide
With Ant, you do this with FilterSets and the <copy> task.
For anyone who needs to modify the diskstore path which cannot be set as the ehcache javadoc states that the diskstore parameter is ignored, you could create your own implementation of a EhCacheManagerFactoryBean, which allows you to inject the diskstore path; you basically need to intercept the creation of the CacheManager and modify the configuration passed along using your diskstore property, e.g.:
private String diskStorePath;
...getter/setter
public void afterPropertiesSet() throws IOException, CacheException {
if (this.shared) {
// Shared CacheManager singleton at the VM level.
if (this.configLocation != null) {
this.cacheManager = CacheManager.create(this.createConfig());
}
else {
this.cacheManager = CacheManager.create();
}
}
else {
// Independent CacheManager instance (the default).
if (this.configLocation != null) {
this.cacheManager = new CacheManager(this.createConfig());
}
else {
this.cacheManager = new CacheManager();
}
}
if (this.cacheManagerName != null) {
this.cacheManager.setName(this.cacheManagerName);
}
}
private Configuration createConfig() throws CacheException, IOException {
Configuration config = ConfigurationFactory.parseConfiguration(this.configLocation.getInputStream());
DiskStoreConfiguration diskStoreConfiguration = config.getDiskStoreConfiguration();
if (diskStoreConfiguration == null) {
DiskStoreConfiguration diskStoreConfigurationParameter = new DiskStoreConfiguration();
diskStoreConfigurationParameter.setPath(getDiskStorePath());
config.addDiskStore(diskStoreConfigurationParameter);
} else {
diskStoreConfiguration.setPath(getDiskStorePath());
}
return config;
}
The Spring config then looks like this:
<bean id="cacheManager" class="com.yourcompany.package.MyEhCacheManagerFactoryBean" depends-on="placeholderConfig">
<property name="diskStorePath" value="${diskstore.path}"/>
<property name="configLocation" value="classpath:ehcache.xml" />
</bean>