Externarlize ehcache.xml to use properties from external properties file - java

I want to place property place holders in the ehcache.xml file (like ${}) so that the values can be replaced from external properties file (.properties) at runtime.
Something like:
ehcache.xml (in classpath):
<defaultCache
maxElementsInMemory="20000"
eternal="false"
timeToIdleSeconds="${default_TTI}"
timeToLiveSeconds="86400"
overflowToDisk="true"
... />
ehcache.properties (outside of the war/classpath):
...
default_TTI=21600
...
The purpose is to be able to change the cache configuration without the need to rebuild the app. Spring's PropertyPlaceHolder will only work with Spring bean definiton for ehcache which I dont want (need to keep the ehcache.xml as a file)
There are similar posts here but nothing got me to solution. I've been searching for a week now!!
Im using Spring 2.5.6,Hibernate 3.2.6 and Ehcache 2.4.6
Any help or idea is greatly Appriciated!!
Thanks a lot,
Tripti.

As a workaroud solution you can set property values to system scope (System.setProperty(...)). EhCahe uses these properties to resolve placeholders during parsing its configuration file.

I got the solution finally! Thanks to braveterry for pointing me in that direction.
I used this at context startup:
Inputstream = new FileInputStream(new File("src/config/ehcache.xml").getAbsolutePath());
cacheManager = CacheManager.create(stream);
in conjuction with hibernate configuration:
<prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.SingletonEhCacheProvider</prop>
This creates a singleton CacheManager from ehcache.xml file outside the context classpath. I was doing this same earlier but was accidently creating another CacheManager before this one using the default ehcache.xml in the classpath.
Thanks,
Tripti.

svaor, I follow what you mean, I define a bean like this:
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="java.lang.System" />
<property name="targetMethod" value="setProperty" />
<property name="arguments">
<list>
<value>system.project_name</value>
<value>${system.project_name}</value>
</list>
</property>
</bean>
system.project_name define in system.properties file which locate in classpath
I also create a ehcache.xml in classpath, in ehcache.xml has the code like this:
<diskStore path="${java.io.tmpdir}/${system.project_name}/cache" />
but when I deploy my project, I find it cann't use the system.project_name define in system.properties, why?

If you just want to read the config in from disk at startup, you can do the following in EHCache 2.5:
InputStream fis =
new FileInputStream(new File("src/config/ehcache.xml").getAbsolutePath());
try
{
CacheManager manager = new CacheManager(fis);
}
finally
{
fis.close();
}

Related

Spring reference a property from a property bean

I have created a spring configuration file that works well.
My next step was to separate user configuration properties from system properties.
I have decided to create additional xml file with beans that will be configured by the user.
I had problem to create few such logical beans encapsulating properties that will be used by real class beans:
I have found over the net an option to reference proprieties in such way:
UserConf.xml
<bean id="numberGuess" class="x...">
<property name="randomNumber" value="5"/>
<!-- other properties -->
</bean>
SystemConf.xml
<import resource="UserConf.xml" />
<bean id="shapeGuess" class="y...">
<property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/>
<!-- other properties -->
</bean>
But my problem is that i need x... class to be something logical that shouldn't be initialized at all, and i don't want it to disclose any info of the class hierarchy of the system since it should be only in use configuration xml file.
Solution1 is to create a Java object representing this proprieties:
public class MyProps(...)
and add a bean parent in the spring system configuration:
<bean id="MyProps" class="path to MyProps"/>
in the user side change the previous bean to be:
<bean id="numberGuess" parent="MyProps">
<property name="randomNumber" value="5"/>
<!-- other properties -->
</bean>
Solution2 is to use flat configuration file just like Database.props, and load it using factory.
Solution3 is to use Spring Property Placeholder configuration to load properties from XML properties file (e.g. example), but here i simply don't know how to get a more complex nested structure of properties (properties need to be separated by different logical names, e.g. minNumber will be defined both under xAlgo and y algo).
I don't like to create new Java class only to deal with this problem or to move my user configuration to a flat props file (i need the xml structure), is their any other solution??
I will answer my own question, since it looks as the best solution for me (and much more simplistic than was suggested)
I will use PropertiesFactoryBean to do the work for me:
e.g.
UserConf.xml
<bean id="numberGuess" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="randomNumber">3</prop>
<!-- other properties -->
</bean>
SystemConf.xml
<import resource="UserConf.xml" />
<bean id="shapeGuess" class="y...">
<property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/>
<!-- other properties -->
</bean>
First if you don't know about the property place holder you should take a look at that. Also #Value("${some.property:defaultvalue}") is something you should look at.
Second the word configuration is ambiguous in Spring. Spring uses this word but they mean developer configuration not user configuration. Despite what people say or think Spring is not a configuration engine.
I'm not sure what your trying to do but you should be aware that your configuration will not be adjusted at runtime which is frequently needed for something like "user" configuration. So most people write their own configuration layer.
Another thing you should take a look at is not using the XML configuration and instead use Java Configuration which will give you way more flexibility.

Determining properties available for Spring Beans?

In the Spring Framework, how do you determine what "properties" and other related values are available to be set in the context.xml file(s)? For example, I need to set the isolation level of a TransactionManager. Would that be:
<property name="isolation" value="SERIALIZABLE" />
<property name="isolation_level" value="Isolation.SERIALIZABLE" />
or some other values?
Each bean represents a class, which you can easily find by class="" attribute. Now you simply open JavaDoc or source code of that class and look for all setters (methods following setFooBar() naming convention). You strip set prefix and un-capitalize the first character, making it fooBar. These are your properties.
In your particular case you are probably talking about PlatformTransactionManager and various implementations it has.
Putting the properties into . properties file is a good way of handling.
First define a properties file in your project structure. It is better to put .properties file with the same directory as spring applicationContext.xml.
Your properties file may seem like this :
isolation = "SERIALIZABLE"
isolation_level = Isolation.SERIALIZABLE
You can access this properties file by defining a spring bean like :
<bean id="applicationProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:YourProperties.properties"/>
</bean>
Finally you can access these properties inside Spring beans like :
<bean id="BeanName" class="YourClass">
<property name="PropertyName1" value="${isolation}"/>
<property name="PropertyName" value="${isolation_level}"/>
</bean>
There is another way to inject these values using annotations.

Extending spring's configuration to hold custom xml settings

My servlet.xml file holds all my spring configuration related information like datasource bean etc.
<bean id="..." class="...">
</bean>
Now my application has other settings that I need to save in a configuration file, is it possible to create my own settings in here or is there a better way?
I want something that loads up once and is very fast to reference in my project.
I need this to store some file paths, and other database settings for things like mongodb etc.
You can use .properties file:
<context:property-placeholder location="file:///my/cfg.properties"/>
If the file contents are:
driver=com.mysql.jdbc.Driver
dbname=mysql:mydb
mysetting=42
You can reference them in Spring XML like this:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>${driver}</value></property>
<property name="url"><value>jdbc:${dbname}</value></property>
</bean>
Reference: 4.8.2.1 Example: the PropertyPlaceholderConfigurer.
You can also inject these properties into your own classes:
#Service
public class MyService {
#Value("${mysetting}")
private int mysetting; //Spring will inject '42' on bean creation
//...
}
Of course you can also use setter-injection like in the example with DriverManagerDataSource if you prefer XML.
Also have a look at: Spring 3.1 M1: Unified Property Management.

Spring context property-placholder ehcahe configuration

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>

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