I've got a Spring bean:
<bean id="sharedBean" class="com.bean.SharedBean">
<constructor-arg name="app-name" value="#{ config['app.name'] }"/>
</bean>
This bean is defined in a jar and used in two apps that both get deployed to the same tomcat server. I've only got one properties file the apps share and I'd like to keep it that way if possible. You might see the problem - I need two values for app.name (one for each web app).
I need to set the app.name independently in each app. I don't mind hard coding the value in the Java (but don't think I can inject the value in that direction). I know I can introduce another properties file at a different path and override but I'm hoping there is a better way I can accomplish this through Spring that will allow me to maintain only one shared properties file.
Possible solutions, in recommended order:
1 - is there a reason why the shared bean is declared in a separate jar file? Certainly, the bean class can be located there, but why not declare the actual bean inside the individual web apps where it's being used?
2 - if you absolutely need to keep the bean definition in the shared jar you can use bean inheritance; (a) change your bean class to use a property rather than a constructor arg:
<bean id="sharedBean" class="com.bean.SharedBean">
<property name="app-name" value="default"/>
</bean>
(b) in your web app context file:
<bean id="instanceBean" parent="sharedBean">
<property name="app-name" value="app1"/>
</bean>
3 - use a Spring profile; in your shared jar context.xml:
<beans profile="app1">
<bean id="sharedBean" class="com.bean.SharedBean">
<constructor-arg name="app-name" value="app1"/>
</bean>
</beans>
<beans profile="app2">
<bean id="sharedBean" class="com.bean.SharedBean">
<constructor-arg name="app-name" value="app2"/>
</bean>
</beans>
note: <beans profile=... MUST be the last entries in your context file.
Add a context param to each web.xml (changing value as appropriate):
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>app1</param-value>
</context-param>
Related
I have a Spring-Boot application that just have a simple rest controller. On this controller, I added the jmx annotations #ManagedResource and #ManagedOperation and it is working fine. It is correctly exposed in Jmx.
This application depends on a "global-commons" library to share many basic functionality to all of our modules.
But if I add the same annotations to a class in this library, it is ignored!
And before you ask, yes the library is imported with the latest change.
There is no error or warning message in the logs.
I am configuring all my beans using an xml file. Both classes are beans defined in the same file.
One is a #RestController. The other one is a simple utility class.
Any idea?
Make sure the classes from the global-commons library as managed by Spring. As long as none of the classes in the library are managed by Spring, the annotions don't have any effect.
I found the problems:
The bean that was not working was defined as an "inner" bean:
<bean id="imMetrics" class="com.imetrik.global.common.metrics.ImGlobalMetrics" init-method="init">
...
<property name="reporterList">
<util:list>
<bean id="jmxReporter" class="com.imetrik.global.common.metrics.reporters.ImJmxReporter">
<property name="registryId" value="metricRegistry1"/>
<property name="durationUnit" value="SECONDS"/>
<property name="rateUnit" value="SECONDS"/>
<property name="domain" value="com.imetrik.global.metric"/>
</bean>
</util:list>
</property>
</bean>
The annotated beans is "jmxReporter".
But if I put it outside as a normal "first level" bean and use a reference instead, it is working.
But it is annoying! Is there a way to make it work even as a inner beans?
I am using spring 3.1 with spring profiles to load the beans. In my app context file, I load the properties like :
<context:property-placeholder order="1" location="classpath*:META-INF/spring/*_${spring.profiles.active}.properties" ignore-unresolvable="true"/>
And then I use the property value to load the data source bean like
<property name="driverClassName" value="${database.driverClassName}"/>
It works fine.
The problem starts when I add a couple of more property placeholders so that properties from some database tables can be loaded.
This uses a properties reference loaded by
<bean id="configFactoryBean"
class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
<constructor-arg ref="globalSystemConfiguration"/>
</bean>
To add to the details, this configFactoryBean uses the datasource to load the properties from the database.
When I do this, I have the following exception:
java.lang.ClassNotFoundException: ${database.driverClassName}
My analysis is that its trying to load the datasource before resolving the property from the first context property placeholder. I may be wrong. Or maybe spring profile variable is not resolved properly.
Can anyone please help me to fix this.
Thanks
Akki
This bug about multiple property placeholders might relate to your problem: https://jira.spring.io/browse/SPR-9989
When using multiple PropertyPlaceholderConfigurer in conjunction with
#Value annotation and default value for placeholders syntax (ie
${key:defaultValue}), only the first PropertyPlaceholderConfigurer is
used. If this configurer does not contain the desired value, it falls
back to #Value default even if the second
PropertyPlaceholderConfigurer contains the value.
Affects Version/s: 3.1.3
Each <context:property-placeholder> creates a new instance of PropertyPlaceholderConfigurer - it gets messy easily. You should have one such thing per application and on application level, not on libraries' one - that makes maintenance much easier.
For more details and a suggestion how to cope with it look here:
http://rostislav-matl.blogspot.cz/2013/06/resolving-properties-with-spring.html
In my application I am using property-placeholder configurer in following way and it works very well. You can try that.
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:META-INF/spring/*_${spring.profiles.active}.properties</value>
</list>
</property>
</bean>
I think this should resolve your problem. :)
Since you have suggested hardcoding the path to the configuration file works, try using the profiles attribute on the tag to selectively include the configuration.
<beans profile="profileName">
<context:property-placeholder order="1" location="classpath*:META-INF/spring/hardcoded.properties" ignore-unresolvable="true"/>
</beans>
<beans profile="profileName2">
<context:property-placeholder order="1" location="classpath*:META-INF/spring/hardcoded.properties" ignore-unresolvable="true"/>
</beans>
See this article explaining profiles: http://java.dzone.com/articles/using-spring-profiles-xml
I'm running tests using spring (SpringJUnit4ClassRunner and #ContextConfiguration). The tests are run in parallel.
Some of my beans are singleton, and I would like to change them to be in scope "thread" of the tests. I want each test to have its own instance of the bean.
I've managed to it by having an applicationContext.xml file and a applicationTestContext.xml file which is used for tests.
In the applicationTestContext.xml I define those beans with scope "thread".
The problem with this is that everytime we add a new bean of that type, we'll have to add it to both applicationContext.xml and applicationTestContext.xml which is pretty annoying.
Is there a way to do it with less boilerplate?
Gather up all the beans whose scope you want to customize and put them in a separate bean config file, included from both applicationContext and applicationTestContext, e.g.
<import resource="customScopedBeans.xml"/>
Then use a placeholder for the scope
<bean class="com.Foo" scope="${threadOrSingleton}" />
and declare the property differently in the parent config file.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<value>threadOrSingleton=thread</value>
</property>
</bean>
I am using Spring 3.0.x to get a WorkManager from an application server and use it to run scheduled jobs that need database access. The problem is, is that this application could be deployed to different Java application servers - in this case, to Websphere 7.0 and GlassFish 3.1 OSE. (Crazy combo, I know...)
The problem I am having is that before deploying to either, I have to change the bean to reference the appropriate TaskExecutor class used for each server. I have Spring load up an applicationContext.xml with these beans in it.
For Websphere, I have to pull the WorkManager externally and use the following bean:
<bean id="myTaskExecutor" class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
<property name="workManagerName" value="wm/default" />
<property name="resourceRef" value="true"/>
</bean>
and the web.xml has something like this:
<resource-ref>
<description>WorkManager</description>
<res-ref-name>wm/default</res-ref-name>
<res-type>commonj.work.WorkManager</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
For GlassFish, I can just use the bean:
<bean id="myTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
</bean>
Can I somehow dynamically change which TaskExecutor implementation is used on the fly?
I could easily check for the Websphere resource to determine if I am on Websphere, then fall back to GlassFish. But how to load a class like that with Spring (using Annotations or otherwise) is baffling me.
Any help is appreciated. Thanks!
P.S. I am not worried about being J2EE compliant for GlassFish - it's just that Websphere forces you to be so (hence pulling an external WorkManager)
You can easily use a custom VM argument to determine what environment you are running in, and use that to load the appropriate context file.
One file for each supported environment, all of which define the same bean.
task-exec-was.xml
task-exec-glassfish.xml
Add a required VM argument.
-Dapp.server=was
Then, wherever you actually load the bean you include the appropriate file based on a PropertyPlaceholderConfigurer.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />
<import resource="task-exec-${app.server}.xml" />
Edits based on information in the comments.
You can override for the environment you do have control over in this case.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="location" value="classpath:spring/was.properties/>
</bean>
<import resource="task-exec-${app.server}.xml" />
Now you have a file called spring/was.properties that defines a property app.server=was, which is read by default. In Glassfish, you supply the same property as a VM argument, which will now override the property read from the file.
I'm using Spring 3.0.5
I have a #ManagedResource bean, for some of the #ManagedAttribute methods which I want to set a defaultValue. Instead of setting it to a hardcoded value I want to be able to read it from a property value at load time, since the default changes from environment to environment.
A snippet from my programs applicationContext.xml:
<context:mbean-export default-domain="sampleApp"/>
<bean id="managedBean" class="com.example.sampleBean">
<constructor-arg value="Sample Bean"/>
<constructor-arg value="${sample.property}"/>
</bean>
I believe I have to use the XML configuration to be able to do this, but haven't figured out how to do it yet.
You can add the following to your applicationContext.xml, it should expose the properties you are after:
<context:property-placeholder location="classpath:application.properties"/>
So if the application.properties file you are pointing to above contains a property called sample.property then Spring will use that to inject into your ${sample.property} placeholder.
For more details you can see the Spring reference here.