Spring xml ...loading variable conditionally - java

My problem is as follows.
In my spring configuration, I would like to read a JVM property. Now based on the JVM property I want to pick a username value defined in some runtime property.
For example,
I have a JVM property defined instanceId. It can have primary or secondary string values.
Also, I have two runtime properties.
PrimaryAccount=123
SecondaryAccount=456
Now based on the jvm property value.
// pseudo code
if instanceId = primary
Bean ABC should be passed 123 in its constructor argument
if instanceId = secondary
Bean ABC should be passed 456 in its constructor argument
I am trying this
<constructor-arg>
<value>#{ systemProperties['newsAppIndexDataNode'].equals('primary') ? ${instance_primary} : ${instance_secondary} }</value>
</constructor-arg>
but I am getting error
Field or property '123' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext'

Use the following Java Configuration
#Configuration
public class SomeConfig {
#Autowired
private Environment environment;
#Bean
public YourBeanType yourBeanType() {
final String jvmProperty = environment.getProperty("instanceId");
if(jvmProperty.equals("primary")) {
return new YourBeanType(123);
}
else if(jvmProperty.equals("secondary")) {
return new YourBeanType(456);
}
return new YourBeanType(-1); //return whatever is meaningfull here, or throw an exception
}
}
EDIT
The XML configuration would probably look like this (not exactly equivalent since id doesn't check for 'secondary'):
<bean class="YourBean">
<constructor-arg index="0" value="#{systemProperties['instanceId'].equals('primary') ? '456' : '123' }">
</bean>

Related

What is the equivalent Java configuration for an XML p-namespace attribute?

For example., how can I convert the following to a JavaConfig #Bean?
<bean id="ldapDao" class="org.mycompany.LdapDAOImpl" p:ldapUrl-ref="ldapHost"/>
Here's where I'm at with the equivalent JavaConfig Bean
#Bean(name="ldapDao")
public LdapDAOImpl getLdapDAOImpl(){
LdapDAOImpl ldapDAOImpl = new LdapDAOImpl();
//How can I set the reference here to ldapHost?
return new LdapDAOImpl();
}
First, the p-namespace is defined as
The p-namespace enables you to use the bean element’s attributes,
instead of nested <property/> elements, to describe your property
values and/or collaborating beans.
In other words, it's just an alternative for defining bean properties.
For example,
<bean id="ldapDao" class="org.mycompany.LdapDAOImpl" p:ldapUrl-ref="ldapHost"/>
is equivalent to
<bean id="ldapDao" class="org.mycompany.LdapDAOImpl">
<property name="ldapUrl" ref="ldapHost" />
</bean>
where the -ref suffix in the p: attribute is explained with an example in the documentation
As you can see, this example includes not only a property value using
the p-namespace, but also uses a special format to declare property
references. Whereas the first bean definition uses <property name="spouse" ref="jane"/>to create a reference from bean john to
bean jane, the second bean definition uses p:spouse-ref="jane" as an
attribute to do the exact same thing. In this case spouse is the
property name, whereas the -ref part indicates that this is not a
straight value but rather a reference to another bean.
Each property element appearing in your <bean> definition requires a corresponding setter in the bean class.
Given all the above, the corresponding #Bean definition would be one that initializes an object of type org.mycompany.LdapDAOImpl and invokes its setLdapUrl setter with the object corresponding to the bean named ldapHost as an argument.
For example, assuming you had such a bean
#Bean
public LdapHost ldapHost() {
return new LdapHost();
}
you'd then use that to initialize your ldapDao
#Bean
public LdapDaoImpl ldapDao() {
LdapDaoImpl ldapDao = new LdapDaoImpl();
ldapDao.setLdapUrl(ldapHost());
return ldapDao;
}
Alternatively, you can have Spring inject it for you.
#Bean
public LdapDaoImpl ldapDao(LdapHost ldapHost) {
LdapDaoImpl ldapDao = new LdapDaoImpl();
ldapDao.setLdapUrl(ldapHost);
return ldapDao;
}
Looks Like the p:ldapUrl-ref is nothing but setting the value of ldapUrl in my LdapDAOImpl class. So simple setter works just fine.
#Bean(name="ldapDao")
public LdapDAOImpl getLdapDAOImpl(){
LdapDAOImpl ldapDAOImpl = new LdapDAOImpl();
ldapDAOImpl.setLdapUrl(url);
return new LdapDAOImpl();
}

Spring: Environment specific configuration

Using Spring I need some kind of environment (dev|test|prod) specific properties.
I have exactly one configuration file (myapp.properties) and for some reasons I cannot have more than one configuration file (even spring can handle more than one).
So I need the possibility to add properties with a prefix like
dev.db.user=foo
prod.db.user=foo
and tell the application which prefix (environment) to use with a VM-argument like -Denv-target or something like this.
I use for this purpose a subcass of PropertyPlaceholderConfigurer:
public class EnvironmentPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
private static final String ENVIRONMENT_NAME = "targetEnvironment";
private String environment;
public EnvironmentPropertyPlaceholderConfigurer() {
super();
String env = resolveSystemProperty(ENVIRONMENT_NAME);
if (StringUtils.isNotEmpty(env)) {
environment = env;
}
}
#Override
protected String resolvePlaceholder(String placeholder, Properties props) {
if (environment != null) {
String value = props.getProperty(String.format("%s.%s", environment, placeholder));
if (value != null) {
return value;
}
}
return super.resolvePlaceholder(placeholder, props);
}
}
and using it in applicationContext.xml (or any other spring-configuration file):
<bean id="propertyPlaceholder"class="EnvironmentPropertyPlaceholderConfigurer">
<property name="location" value="classpath:my.properties" />
</bean>
In my.properties you can define properties like:
db.driverClassName=org.mariadb.jdbc.Driver
db.url=jdbc:mysql:///MyDB
db.username=user
db.password=secret
prod.db.username=prod-user
prod.db.password=verysecret
test.db.password=notsosecret
Thereby you can prefix properties keys by an environment key (e.g. prod).
Using the vm argument targetEnvironment you can choose the enviroment you like to use, e.g. -DtargetEnvironment=prod.
If no environment-specific-property exists, the default one (without a prefix) is choosen. (You should always define a default one.)
I don't know what are your constraints to avoid having more than one configuration file but you can use something like -Denvtarget=someValue and in java do:
//Obtain the value set in the VM argument
String envTarget= System.getProperty("env-target");
Properties properties;
try {
properties = PropertiesLoaderUtils.loadAllProperties("myapp.properties");
} catch (IOException exception) {
//log here that the property file does not exist.
}
//use here the prefix set in the VM argument.
String dbUser = properties.getProperty(envTarget+".db.user");
If you have environment variable and want to get property according this variable you can declare your properties that way:
<property name="username" value="${${env-target}.database.username}" />
<property name="password" value="${${env-target}.database.password}" />
Also make sure that you use properly configured property-placeholder:
<context:property-placeholder location="classpath*:META-INF/spring/*.properties"/>
Or, if you use special property configurer (e.g. EncryptablePropertyPlaceholderConfigurer), set properties:
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
But as mentioned earlier it is better to use profiles.

Spring bean scope based on dynamic constructor value

I have to create a bean where it needs to be cached based on the dynamic constructor value. Example: I need an OrganizationResource bean where "x" (constructor value) organization will have its own specific instance values and "y" (constructor value) will have different values.
But I don't want to create a new object for every x value, I want it to be cached.
I know there are 2 scopes, singleton and prototype, for dynamic constructor value. I am planning to use prototype, but it seems it will create a new object every time, how can I implement cache based on constructor value in spring?
FactoryBean is a way to go. It is very simple, give it a try. All you have to do is create a class implementing FactoryBean and reference it in bean definition file:
package some.package;
import org.springframework.beans.factory.FactoryBean;
public class ExampleFactory implements FactoryBean {
private String type;
public Object getObject() throws Exception {
//Logic to return beans based on 'type'
}
public Class getObjectType() {
return YourBaseType.class;
}
public boolean isSingleton() {
//set false to make sure Spring will not cache instances for you.
return false;
}
public void setType(final String type) {
this.type = type;
}}
Now, in your bean definition file, put:
<bean id="cached1" class="some.package.ExampleFactory">
<property name="type" value="X" />
</bean>
<bean id="cached2" class="some.package.ExampleFactory">
<property name="type" value="Y" />
</bean>
It will make objects based on strategy you implemented in ExampleFactory.getObject().

spring bean with dynamic constructor value

I need to create an Object which is in-complete without the constructor argument. Something like this
Class A {
private final int timeOut
public A(int timeout)
{
this.timeOut = timeout;
}
//...
}
I would like this Bean to be spring managed, so that I can use Spring AOP later.
<bean id="myBean" class="A" singleton="false">
</bean>
However my bean needs timeout to be passed as a dynamic value - is there a way to create a spring managed bean with dynamic value being injedcted in the constructor?
BeanFactory has a getBean(String name, Object... args) method which, according to the javadoc, allows you to specify constructor arguments which are used to override the bean definition's own arguments. So you could put a default value in the beans file, and then specify the "real" runtime values when required, e.g.
<bean id="myBean" class="A" scope="prototype">
<constructor-arg value="0"/> <!-- dummy value -->
</bean>
and then:
getBean("myBean", myTimeoutValue);
I haven't tried this myself, but it should work.
P.S. scope="prototype" is now preferable to singleton="false", which is deprecated syntax - it's more explicit, but does the same thing.
Two options spring (no pun intended) to mind:
1. create a timeout factory, and use that as the constructor parameter.
You can create a bean which implements FactoryBean, and it's job is to create other beans. So if you had something that generates salt's for encryption, you could have it return from getObject() a EncryptionSalt object. In your case you're wanting to generate Integers.
Here is an example: http://www.java2s.com/Code/Java/Spring/SpringFactoryBeanDemo.htm
2. create a timeout bean that wraps an int that's dynamically set, and leave that in the "prototype" state so it's created each time
Instead of going to the hassle of creating a factory, the EncryptionSalt object could just be declared as a prototype bean, so when it's injected a new object is created each time. Place the logic into the constructor or somewhere else.
It somewhat depends what value you want the timeout to actually be.
Do it explicitly:
interface Bean {
void setTimeout(int timeout);
}
class BeanImpl implements Bean {
private int timeout;
#Override
public void setTimeout(int timeout) {
this.timeout = timeout;
}
...
}
<bean id="bean" class="BeanImpl" scope="prototype">
...
<!-- Nothing about timeout here -->
...
</bean>
class Client {
private Bean bean;
public void setBean(Bean bean) {
this.bean = bean;
}
...
public void methodThatUsesBean() {
int timeout = calculateTimeout();
bean.setTimeout(timeout);
...
}
}

Logging bean id into log4j logfile without BeanNameAware interface

Given a set of classes wired together by spring. There are several classes that are used with different configuration in multiple instances in the environment. They have different beanid of course.
The problems:
When they make log entries, we dont know exactly which bean made the log, since the log4j displays the classname only
I know that I could use logger instantiated by spring InitializationBean+BeanNameAware interface methods, but I do not want to do it, since I do not want to implement them in all classes
The solution could be:
Having some effect on bean factory to store the id of the beans in a map with the bean reference (key is the ref, name is the value)
Creating an aspect to be applied on every method, that would set an "BeanName" MDC entry in Log4j before the call, and would restore it to the previous value after the call. Meanwhile the previous beannames could be stored in a threadlocal in a stack.
The questions:
How can I change/configure the bean factory to do this trick for me? Is there any customization point I could use to this aim?
How can I avoid memory leaks in the map in the beanid registry? Maybe the registry is not needed at all, if somehow spring can look up the id for a reference.
Do you have any better idea, that would not result in changing ten thousand classes?
Thanks in advance.
UPDATE:
- Does anyone have solution for the prototype beans?
I have managed to hack something together based on this Spring AOP Example.
I am not yet up to speed with Spring 3 so I have implemented this using Spring 2.5 - I dare say there are more elegant ways of achieving what you want. I have implemented this using System.out's for simplicity but these could easily be converted to log4j calls.
Initially I create a map between the Spring's bean names and the string representation of the object (InitBean). This map is used inside the MethodInterceptor - I did try making the MethodInterceptor an InitializingBean but the MethodInterceptor stopped working for some reason.
Performing an equals between the bean passed in via the MethodInterceptor and the other beans in the application context did not work. e.g. by using something like "ctx.getBeansOfType(GoBean.class)" inside the MethodInterceptor. I presume this is because the object passed in via the MethodInvocation was a GoBean whereas objects obtained from the application context at this point are proxied (e.g. something like example.GoBean$$EnhancerByCGLIB$$bd27d40e).
This is why I had to resort to a comparison of object string representations (which is not ideal). Also I specifically do not want to activate the MethodInterceptor logic when calling the "toString" method on an object (as since I'm using toString elsewhere leads to infinite loops and StackOverflow).
I hope this is useful,
applicationContext.xml
<beans>
<bean name="initBean" class="example.InitBean"/>
<bean name="methodLoggingInterceptor" class="example.MethodLoggingInterceptor">
<property name="initBean" ref="initBean"/>
</bean>
<bean name="proxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>go*</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>methodLoggingInterceptor</value>
</list>
</property>
</bean>
<bean name="goBean1" class="example.GoBean" />
<bean name="goBean2" class="example.GoBean" />
<bean name="goBean3" class="example.GoBean" />
</beans>
GoBean.java
public class GoBean {
public void execute(){
System.out.println(new Date());
}
}
SimpleTestClass.java
public static void main( String[] args ){
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ArrayList<GoBean> goBeans = new ArrayList<GoBean>();
goBeans.add((GoBean) ctx.getBean("goBean1"));
goBeans.add((GoBean) ctx.getBean("goBean2"));
goBeans.add((GoBean) ctx.getBean("goBean3"));
for(GoBean g: goBeans){
g.execute();
}
}
InitBean.java
public class InitBean implements ApplicationContextAware, InitializingBean {
private ApplicationContext ctx;
private Map<String, String> beanMap = new HashMap<String,String>();
public void setApplicationContext(ApplicationContext ac) throws BeansException {
ctx = ac;
}
public void afterPropertiesSet() throws Exception {
for(String beanName: ctx.getBeanNamesForType(GoBean.class)){
beanMap.put(ctx.getBean(beanName).toString(), beanName);
}
}
public Map<String,String> getBeanMap(){
return beanMap;
}
}
MethodLoggingInterceptor.java
public class MethodLoggingInterceptor implements MethodInterceptor{
private InitBean initBean;
public Object invoke(MethodInvocation method) throws Throwable {
if (!"toString".equals(method.getMethod().getName())) {
StringBuilder sb = new StringBuilder();
Object obj = method.getThis();
if (obj instanceof GoBean) {
Map<String,String> beanMap = initBean.getBeanMap();
String objToString = obj.toString();
if (beanMap.containsKey(objToString)) {
System.out.println(beanMap.get(objToString));
sb.append("bean: ");
sb.append(beanMap.get(objToString));
sb.append(" : ");
}
}
sb.append(method.getMethod().getDeclaringClass());
sb.append('.');
sb.append(method.getMethod().getName());
System.out.println(sb.toString() + " starts");
Object result = method.proceed();
System.out.println(sb.toString() + " finished");
return result;
} else {
return method.proceed();
}
}
public void setInitBean(InitBean ib) {
this.initBean = ib;
}
}

Categories