Spring context property-placholder ehcahe configuration - java

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>

Related

Reading value from file into applicationContext.xml file

I have a spring based web application and in my application context xml file, I have defined a bean which has all the parameters to connect to database. As part of this bean, for one of the parameters, I have a password key, as shown in the below example and I wanted the value should come from a /vault/password file. This /vault/password is not part of the project/application. This /vault/password will be there in host machine by default.
What is the syntax in applicationContext.xml bean definition, to read a value from a file outside of application context.
<bean class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" id="dataSource">
<property name="url" value="jdbc:postgresql://postgres:5432/" />
<property name="username" value="postgres" />
<property name="password" value="/vault/password" />
</bean>
Something like this is probably your best bet:
How to correctly override BasicDataSource for Spring and Hibernate
PROBLEM:
Now I need to provide custom data source based on server environment
(not config), for which I need to calculate driverClassName and url
fields based on some condition.
SOLUTION:
Create a factory (since you need to customize only the creation phase
of the object, you don't need to control the whole lifetime of it).
public class MyDataSourceFactory {
public DataSource createDataSource() {
BasicDataSource target = new BasicDataSource();
if (condition) {
target.setDriverClassName("com.mysql.jdbc.Driver");
target.setUrl("jdbc:mysql://localhost/test?relaxAutoCommit=true");
} else { ... }
return target;
}
}
In your case, your customization would do some I/O to set target.password.

Spring bean customization with configurable properties

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");

How to translate xml configuration bean to Java config bean?

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.

How to load system properties file in Spring?

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
...

Import Spring config file based on property in .properties file

In my Spring xml configuration I'm trying to get something like this to work:
<beans>
<import resource="${file.to.import}" />
<!-- Other bean definitions -->
</beans>
I want to decide which file to import based on a property in a properties file.
I know that I can use a System property, but I can't add a property to the JVM at startup.
Note: The PropertyPlaceHolderConfigurer will not work. Imports are resolved before any BeanFactoryPostProcessors are run. The import element can only resolve System.properties.
Does anyone have a simple solution to this? I don't want to start subclassing framework classes and so on...
Thanks
This is, unfortunately, a lot harder than it should be. In my application I accomplished this by doing the following:
A small, "bootstrap" context that is responsible for loading a PropertyPlaceholderConfigurer bean and another bean that is responsible for bootstrapping the application context.
The 2nd bean mentioned above takes as input the "real" spring context files to load. I have my spring context files organized so that the configurable part is well known and in the same place. For example, I might have 3 config files: one.onpremise.xml, one.hosted.xml, one.multitenant.xml. The bean programmatically loads these context files into the current application context.
This works because the context files are specified as input the the bean responsible for loading them. It won't work if you just try to do an import, as you mentioned, but this has the same effect with slightly more work. The bootstrap class looks something like this:
public class Bootstrapper implements ApplicationContextAware, InitializingBean {
private WebApplicationContext context;
private String[] configLocations;
private String[] testConfigLocations;
private boolean loadTestConfigurations;
public void setConfigLocations(final String[] configLocations) {
this.configLocations = configLocations;
}
public void setTestConfigLocations(final String[] testConfigLocations) {
this.testConfigLocations = testConfigLocations;
}
public void setLoadTestConfigurations(final boolean loadTestConfigurations) {
this.loadTestConfigurations = loadTestConfigurations;
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
context = (WebApplicationContext) applicationContext;
}
#Override
public void afterPropertiesSet() throws Exception {
String[] configsToLoad = configLocations;
if (loadTestConfigurations) {
configsToLoad = new String[configLocations.length + testConfigLocations.length];
arraycopy(configLocations, 0, configsToLoad, 0, configLocations.length);
arraycopy(testConfigLocations, 0, configsToLoad, configLocations.length, testConfigLocations.length);
}
context.setConfigLocations(configsToLoad);
context.refresh();
}
}
Basically, get the application context, set its config locations, and tell it to refresh itself. This works perfectly in my application.
Hope this helps.
For the Spring 2.5 and 3.0, I have a similar solution to louis, however I've just read about 3.1's upcoming feature: property management, which sounds great too.
There is an old issue on the Spring JIRA for adding properties placeholder support for import (SPR-1358) that was resolved as "Won't Fix", but there has since been a proposed solution using an EagerPropertyPlaceholderConfigurer.
I've been lobbying to have SPR-1358 reopened, but no response so far. Perhaps if others added their use cases to the issue comments that would help raise awareness.
Why not:
read your properties file on startup
that will determine which Spring config to load
whichever Spring config is loaded sets specific stuff, then loads a common Spring config
so you're effectively inverting your current proposed solution.
Add something similar to the following:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound"><value>true</value></property>
<property name="locations">
<list>
<value>classpath:propertyfile.properties</value>
</list>
</property>
</bean>
If what you want is to specify the imported XML file name outside applicationContext.xml so that you could replace applicationContext.xml without losing the configuration of the imported XML file path, you can just add an intermediate Spring beans XML file, say, confSelector.xml, so that applicationContext.xml imports confSelector.xml and confSelector.xml only contains an <import> element that refers to the suitable custom beans XML file.
Another means that might be of use are XML entities (defined by adding <!ENTITY ... > elements into the DTD declaration at the beginning of XML). These allow importing XML fragments from other files and provide "property placeholder"-like functionality for any XML file.
Neither of these solutions allows you to have the configuration file in Java's .properties format, though.
André Schuster's answer, which I bumped, helped me solve a very similar issue I was having in wanting to find a different expression of properties depending on whether I was running on my own host, by Jenkins on our build host or in "real" deployment. I did this:
<context:property-placeholder location="file:///etc/myplace/database.properties" />
followed later by
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>WEB-INF/classes/resources/database.properties</value>
...
</list>
</property>
</bean>
which solved my problem because on my development host, I put a link to my own copy of database.properties in /etc/myplace/database.properties, and a slightly different one on the server running Jenkins. In real deployment, no such file is found, so Spring falls back on the "real" one in resources in my class files subdirectory. If the properties in question have already been specified by the file on /etc/myplace/database.properties, then (fortunately) they aren't redefined by the local file.
Another workaround which does not rely on system properties is to load the properties of all the files using a different PropertyPlaceholderConfigurer for each file and define a different placeholderPrefix for each of them.
That placeholderprefix being configured by the initial property file.
Define the first property file: (containing either first or second)
global.properties
fileToUse=first
Define the files containing a property that can be switched depending on the property defined just above:
first.properties
aProperty=propertyContentOfFirst
second.properties
aProperty=propertyContentOfSecond
Then define the place holders for all the files:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:global.properties</value>
</list>
</property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="placeholderPrefix" value="first{" />
<property name="locations">
<list>
<value>classpath:first.properties</value>
</list>
</property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="placeholderPrefix" value="second{" />
<property name="locations">
<list>
<value>classpath:second.properties</value>
</list>
</property>
</bean>
Use the property defined in global to identify the resource to use from the other file:
${fileToUse}{aProperty}
If I add the JVM argument below and have the file myApplicationContext.dev.xml, spring does load
-DmyEnvironment=dev
<context:property-placeholder />
<import resource="classpath:/resources/spring/myApplicationContext.${myEnvironment}.xml"/>
I'm using Spring 3 and load a properties like that:
<context:property-placeholder location="/WEB-INF/my.properties" />

Categories