property placeholder in class attribute of a bean - java

A colleague of mine and I are playing around with the following spring configuration:
<beans>
<context:property-placeholder location='classpath:/configuration.properties'/>
<bean id="myBean" class="${type}" />
</beans>
We want to be able to provide a environment specific implementation of myBean. On a developers system the value of type would be a lightweight implementation of whatever myBean does. And in a production environment we would use a full-blown version.
When my colleague runs the code, everything works. When I run the code, I get a ClassNotFoundException, because spring tries to instantiate ${type}.class. And it is not like it sometimes works and sometimes does not. On my machine it always fails and on my colleagues machine it always works.
Does anybody knows what the problem is?
Thx in advance,
Yevgeniy
UPDATE
as requested, here is how we load the application context:
ClassPathXmlApplicationContext("application-configuration.xml");
the content of the properties file is pretty simple:
type=foobar.TestServiceImpl

Instead of trying to override the class with a placeholder, I would like to suggest an alternative approach for your problem. You could use the Profile functionality of Spring.
It would be simplier and safer to change the class depending of the environment.
<beans>
<beans profile="dev">
<bean id="myBean" class="dev.impl.MyBean" />
</beans
<beans profile="prod">
<bean id="myBean" class="prod.impl.MyBean" />
</beans
</beans>
You can then activate a given profile in development by adding the following system property to your server -Dspring.profiles.active="dev".
You can define a default profile which will be used by adding the following to your web.xml:
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>prod</param-value>
</context-param>

The following assumes Spring 3.1+.
I can tell you this much for sure. For Spring to fail with a ClassNotFoundException for class
${type}
means that it did not resolve the placeholder.
When you specify
<context:property-placeholder location='classpath:/configuration.properties'/>
Spring uses a PropertyPlaceholderBeanDefinitionParser to register either a PropertySourcesPlaceholderConfigurer or a PropertyPlaceholderConfigurer bean which will do the placeholder resolution.
PropertySourcesPlaceholderConfigurer is a BeanFactoryPostProcessor. This mean that it can modify bean definitions. Without going into much detail, if it cannot resolve a placeholder, the process fails with an IllegalArgumentException that states that the placeholder could not be resolved.
If you're saying that the ${type} wasn't resolved, then no PropertySourcesPlaceholderConfigurer or PropertyPlaceholderConfigurer beans were created. This probably means your context does not have
<context:property-placeholder location='classpath:/configuration.properties'/>
With the information you've shown us, that is what I think is going on. If you can prove otherwise, I'll ask you to provide a small reproducible example. Ideally, you would show the contents of your compiled project.

As far as I remember property placeholder fails mutually. This means that if it cannot locate file configuration.properties on your machine the property is just not initialized.
To approve this assumption try to "break" the application on your colleague's machine: change the location to something wrong, e.g. classpath:/configuration12345.properties. I believe that the problem will appear on his machine too.
Now, check what's wrong in your environment and why this file cannot be found there.
BUT: do you have something against profiles? Spring provides a cool feature that is attended exactly for your use-case: spring profiles.

Related

Which bean is loaded if profile is not set?

Testing the below Spring profile :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<bean id="bean1"
class="com.Test2">
</bean>
<beans profile="DEV">
<bean id="bean1"
class="com.Test1">
</bean>
</beans>
<bean id="bean1"
class="com.Test2">
</bean>
</beans>
It seems that com.Test2 is loaded if a Spring profile is not set. Is this the expected behavior ?
I'm just trying to understand how Spring loads classes if profiles are set/unset. It seems that if a profile is not set then Spring will create the class if it exists outside the profile, if a profile is set then Spring will create the class for the profile. If the class also exists outside the profile it is not loaded when the profile is loaded.
So, in the above example if DEV profile is set then com.test1 is loaded for bean id bean1, if no profile is set then com.test2 is loaded for bean1. Is this the expected behavior.
The behavior you described is the expected one.
Usually, in Spring there is a rule of thumb related with bean loading: every bean that is loaded with the same name as another one, and that is processed later, will override the older one.
The key term here is being processed later.
In your specific use case, first, every bean that is not defined in any profile will be included, at first glance, in the Spring context.
When you activate a profile, as in your example, you are making visible a new piece of configuration: if this configuration contains a bean with the same name as other one already processed, as indicated, it will override this bean in the Spring context.
This fact is always true independently of the mechanism, Java, XML configuration, or both, you use to define the beans.
It is important to note that the order in which Spring process the different configurations that can be found across the code and different libraries is not deterministic. In your specific use case, when using XML configurations, you can safely assume that it will load the different configurations in the order in which they are imported in your main configuration files (the ones configured for the context load mechanism you choose) and, for every one of them, in the order in which the beans are defined within the same XML file, if that is the case.
This general override rule is always true except in the case you use Spring Boot 2: in this case, if you override a bean by name, by default, an exception will be raised indicating that a bean with this name is already defined in the Spring context. You can restore the usual override behavior by specifying the following configuration property:
spring.main.allow-bean-definition-overriding=true
In addition to profiles, Spring Boot will allow you to load a bean or not depending on several types of conditions. This mechanism is applied usually when loading #Configurations in the auto configuration process.
Yes it is the expected behaviour, Any bean that does not specify a profile belongs to the "default" profile. All the beans with no #profile annotation (or for XML) specified belongs to the default profile.
If you don't specify explicitly a profile then "default" profile is loaded that belong to all beans that are not in scope of any profiles.
So yes, if you run your application without any profile bean1 will be loaded as an instance of com.Test2, in other hand by configuring a profile all beans that are not in scope of a profile ("default") will be replaced with that once that matches definition (in your case com.Test1).
For more details check:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-definition-profiles
https://www.baeldung.com/spring-profiles

Getting bean descriptions from a spring application context xml without initializing the beans?

Is there a decent way to read a spring application context from xml without initializing the beans right away, so they can be mocked or not, before they are actually created?
Yes, I know about lazy-init, but the actual applicationContext.xml is taboo for me.
The situation is that I have to create some JUnit tests on an application which has been created in a way that puts some difficulties in the way:
the configuration file must not be altered, nor the code that is to be tested
there are a lot of beans, some of them rather complex and hard to mock
part of the test is to use as many of the beans un-mocked as possible
some of the beans implement InitializingBean, verifying the environment on initialization and throw errors when Jenkins tries to build.
new FileSystemXmlApplicationContext("config.xml") immediately initializes the beans and throws errors if not in an appropriate environment.
What I have tried:
I have tried Powermock whenNew to mock the offending beans but to do that I would have to know the class which actually creates the beans. As this class belongs to the spring framework, it may change with future versions. When using #PrepareEverythingForTest it results in an StackOverflow exception. The application is real life, not a small piece of code from some tutorial.
I also searched for something like ForceLazyFileSystemXmlApplicationContext but didn't find anything.
Pleas don't start nagging about bad design, I know about that.
You can write your own applicationContext for your testing purpose. There you need to write your own BeanFactory. In that factory you can replace some of beans with mocks.
I just remembered about an option. Evaluate if you can use spring profiles. It will allow to choose a different implementation based on profiles.
Example:
<!-- This is the default myBean -->
<beans>
<bean id="myBean" class="mypackage.MyBean" />
</beans>
<!-- This is the mocked myBean for testing purposes, it will take place when testingProfile is active -->
<beans profile="testingProfile">
<bean id="myBean" class="mypackage.MyBeanMock" />
</beans>
You can indicate which profile to use via properties or environment variables. Example, if you are using maven in your project you could run the tests as:
mvn test -Dspring.profiles.active="testingProfile"
Take a look at:
http://www.baeldung.com/spring-profiles
https://spring.io/blog/2011/02/11/spring-framework-3-1-m1-released/

Referencing a value from a spring config

First a bit of setup info:
I have a multi-tenant spring based application. The multi-tenant enabling library is an in-house developed tool where I work that I have to use. How it works is that there is an interceptor that sets in front of the servlet for the application. Upon a request hitting the servlet it loads a tenant specific spring config for "stuff" needed for the tenant specified on the url hitting the servlet.
As stated, the above is just a bit of background. Now to the issue/question:
What I want to do is to create, in the tenant configuration that is loaded, a value that I can use to inject where I need. So, is there a way I can just define a constant in a spring config and then reference it via #Value or #Resource in java code?
There will be no bean implementation behind it, it would just be purely and only a key/value that I can reference where needed in my application by name. So, something to the effect of:
<bean name="MyIdentifier">
<property name="theId" value="1001" />
</bean>
And then can I do something like?
#Value{MyIdentifier.theId}
String theId;
And have Spring be aware of and inject the value. The problem is that doing something like above Spring complains there is no implementation for the bean. Notice, no class specified for the bean. The reason I want to do this is every tenant config file will contain this bean, but the actual value will vary per tenant.
Is there some other type to use in the config to do this? If so, what schemas have to be on the config?
I am guessing I am either trying to make Spring do something not intended, or, this is so simple I cannot see it since I have stared at it too long. Anyway, thanks for the help.
You can not create bean tag in configuration file without providing class implementation. If you want to inject the value of fields, you have to go for properties file instead.
Create property file as below:
application.properties
theId=1001
Load property file in your configuration:
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath:application.properties</value>
</list>
</property>
</bean>
And access the property in your concrete class:
#Value("${theId}")
String theId;

How does Spring start this app?

I'm parsing through some inherited code of a Java app that is deployed as a WAR file in JBOSS, and uses Spring, JMS, and HornetQ. I'm trying to figure out, for lack of a better phrase, what makes the app "go". Most of the Spring examples I've seen include an application with a main() method, which imperatively acts on the beans provided by the Spring context in some way. This application has no main(). As far as I can tell, this is what's happening:
The WAR file's web.xml uses a listener to launch Spring when the application starts in JBOSS, and passes it a config file
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-context.xml</param-value>
</context-param>
Spring then processes the application-context.xml file, which includes this snippet:
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="destination" />
<property name="messageListener" ref="appListenerProxy" />
Through a couple more references in the application-context.xml, the "appListenerProxy" ultimately refers to a class in the application that implements SessionAwareMessageListener, which responds to messages on a queue.
The part that's tripping me up is that I don't see any kind of code to get the jmsContainer bean and do something with it. Is that a well-defined ID, such that the Spring framework is looking for it? And if so, is that documented somewhere (along with other IDs the framework might be looking for)? http://docs.spring.io/spring/docs/current/spring-framework-reference/html/jms.html seemed to be the closest I found, but it doesn't really specify whether that ID is just in the examples by convention, or if it's a meaningful string.
Thanks
Containers in spring are part of the framework core. The Framework will scan all the containers that implement a certain container interface, and initialize them.
This also relies on the Inversion of Control (IoC) principle.
For more information on IoC Container, check this page:
IoC Container
You don't really need to do anything with DefaultMessageListenerContainer bean, its frame work part.
The DefaultMessageListenerContainer bean establish JMS session with your JMS destination on application startup. When message are received in your destination it invokes the messageListener onMessage method- in your case appListenerProxy bean.
read here.

Is there a way to pass the file location by jndi in a <util:properties /> tag?

I have the following properties declared in my spring-config.xml
<util:properties id="ldap" location="classpath:com/company/project/resources/some_configuration.properties"/>
Then I inject the values contained in the properties into some member variables using the spring #Value annotation in a service implementation (this approach is by far the cleanest/most elegant I have used in the implementation of the service and if possible I wouldn't want to change it).
The problem with this layout is that I have to modify the properties file and regenerate the application war for every deployment environment (quality, production, etc) and the server admins want to configure the some_configuration.properties path by JNDI (the application server is JBoss)
How can I pass the file location by jndi in the <util:properties /> tag?
Any help and suggestions would be appreciated
edit:
It would be nice if somebody comes out with a solution where I could do something like:
<util:properties id="ldap" location="jndi:url/some_configuration.properties"/>
Or similar
Old post, but this may be useful for others:
<jee:jndi-lookup id="ldapProps" jndi-name="your/jndi" resource-ref="true"/>
<util:properties id="ldap" location="file://#{ldapProps}/some_configuration.properties" />
I was looking something similar, this answer will help you using PropertyPlaceholderConfigurer: https://stackoverflow.com/a/3486315/439427.
HTH
In your case you will need to configure the PropertyPlaceholderConfigurer in your beans then you will just need to do the following change:
<util:properties id="ldap"
location="classpath:x/y/z/resources/${environment}.properties"/>
Where ${environment} will be set by an environment variable like this: -Denvironment=dev

Categories