Spring XML Initialization order and Bean overridden - spring

I am trying to understand a web system using spring and I can not debug it.
Now I am confused by the order of the XML initialization.
Support I have an web.xml which is like this:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:/com/pathA/**/applicationContext*.xml,classpath*:/com/pathB/**/applicationContext*.xml
</param-value>
</context-param>
and in /com/pathA I have some xml files that define some beans with the same name.
In /com/pathB I also have some xml files that define beans with the same name as in com/pathA's xml file.
I know that spring framework will use the last bean definition by default.But I can't
find the order of the xml files initialization.
Here is the beans' definition:
/com/pathA/applicationContextOne.xml
<bean name="/testBean" class="com.TestActionOne">
</bean>
/com/pathA/applicationContextTwo.xml
<bean name="/testBean" class="com.TestActionTwo">
</bean>
/com/pathB/applicationContextThree.xml
<bean name="/testBean" class="com.TestActionThree">
</bean>
can anyone can tell me the initialization order of the differnet xml files in /com/pathA/
and the initialization order of the xml files between com/pathA/ and com/pathB?

I have written some samples in local pc (Windows and AIX)to simulate the initialization order and I found the result below.
1.the initialization order of files in different path depends on the order written in
classpath*:/com/pathA/*/applicationContext.xml
on my question, the [applicationContext*.xml] files in pathA will be initialized first and then in pathB.
2.the initialization order of files in the same path is the order of the file name
for example,if in pathA there are [applicatinContextOne.xml] and [applicationContextTwo.xml],then [applicatinContextOne.xml] will be initialized first and then [applicationContextTwo.xml].
I tried this under both Windows and AIX, and it returns the same result.
Hope this will be helpful.

Related

environment specific log4j configuration by spring

I am loading log4j.xml using the traditional way
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:conf/log4j.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
This works fine but now I need to load a different log4j.xml file based on which environment I am in which is defined by a environment variable/jndi entry .. so I was hoping that with new spring 3.1 property management I could just change this to
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:conf/log4j-${ENV-NAME}.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
and spring will load the correct log4j file in each environment but this doesnt works probably because web.xml is loaded before spring. I came across this method
<bean id="log4jInitialization" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.springframework.util.Log4jConfigurer" />
<property name="targetMethod" value="initLogging" />
<property name="arguments">
<list>
<value>log4j-${ENV-NAME}.xml</value>
</list>
</property>
</bean>
so essentially moving configuration of log4j into spring context file instead of web.xml. But this doesnt works either for some reason logging gets enabled in a way that everything is logged. So how can I use a different log4j.xml file based on an environment variable or loading it programmatically in the servletcontextlistener.
It's too late to help adeelmahmood, but hope others will benefit from my answer.
The thing is adeelmahmood was right, this configuration:
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:conf/log4j-${ENV-NAME}.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
is working, but:
Log4jConfigListener should be registered before ContextLoaderListener in web.xml !!!
You must also remember that Log4jConfigListener assumes an expanded WAR file.
Then, if ENV-NAME environment variable is set, it works as expected.
By the way, ${ENV-NAME} can be used in spring config files as well! That's not everything... You can also use our env.property to set spring profile:
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>${ENV-NAME}</param-value>
</context-param>
This way you can set log4jConfigLocation and spring profile, both with one and the same env. variable.
BTW and just in case: using Maven profiles to have separate builds for dev, test and production isn't good practice. Read eg. http://java.dzone.com/articles/maven-profile-best-practices and remember: "Use profiles to manage build-time variables, not run-time variables and not (with RARE exceptions) alternative versions of your artifact".
This might help too for more recent readers: in Log4j 2.7 (but it's probably older) the parameter name has been altered:
see interface Log4jWebSupport :
/**
* The {#link javax.servlet.ServletContext} parameter name for the location of the configuration.
*/
String LOG4J_CONFIG_LOCATION = "log4jConfiguration";
By default, Spring Boot picks up the native configuration from its default location for the system (such as classpath:logback.xml for Logback), but you can set the location of the config file by using the logging.config property.
logging.config=classpath:log4j-<ENV-NAME>.xml
https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/reference/html/howto-logging.html

Using multiple applicationContexts with duplicate bean ids from different jar files in a webApplication

I have bean1 and bean2 defined in applicationContext1 in jar1 and have bean1 (of course a different class) and bean3 defined in applicationContext2 in jar2
I need to use both jar1 and jar2 in my webapplication, which also has an applicationContext3.
I use the below entries in web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext1.xml
<param-value>applicationContext2.xml
<param-value>/WEB-INF/properties/application/applicationContext3.xml
</param-value>
</context-param>
Is there a way to prefix the applicationContext IDs so that they dont step over each other;
for eg:
in applicationContext3 can I have
<bean id="myBean" >
<property name="bean1" ref="ac1:bean1">
<property name="bean2" ref="ac2:bean1">
</bean>
Thanks in advance
Maybe you could arrange it somehow (Spring is very flexible and powerful) but I think you should clean up a little your contexts.
If you are in total control of both libraries and the main app, I would recommend creating only a main applicationContext and copying the relevant beans from the JARs.
Other good solution is to separate every applicationContext in context modules, like repository-context.xml, business-context.xml, etc and reference only the ones you need, so you have more control over what is getting loaded.
Also, take a look at this:
http://www.gridshore.nl/2008/05/13/spring-application-context-loading-tricks/

Reading 1 bean from other bean defined in different xml file in different directory

How can we read one from other bean defined in a different xml file in a different directory. For example, if there is a bean defined in
src\main\resources\serviceconfig\org\project\core_spring\file1.xml
that needs to reference another bean defined in
src\main\java\org\project\core\commons\persistence_spring\file2.xml
Is there any way to reference it or to import the resources of file2.xml into file1.xml?
Specifying the whole relative classpath as in
<import resource="\org\project\core\commons\persistence_spring\file2.xml" />
solved the issue for me.
Still haven't tried the answer given by Chao though. Would be great if that one works because entering relative path and importing some resource everytime I need to refer to it can be quite cumbersome.
You can import file2.xml into file1.xml. Using
<import resource="classpath:file2.xml" />
After importing you can use bean defined in file2.xml in file1.xml.
you can use the <context-param> tag in web.xml to include multiple bean definition files in your application. You can refer to both contexts both on the classpath or in your web folder. and you will be able to use the beans defined in those files as if they were defined in one file.
For example:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/com/your-company/file1.xml
/WEB-INF/file2.xml
</param-value>
</context-param>

Trouble integrating a library using Apache Commons Config with Spring project using PropertyPlaceholderConfigurer

I have a custom library that sends a JMS notification. The idea is that you can use the library by simply by setting some properties in your project.
The custom library is using the Apache Commons Configuration. (The reason I mention this is that I suspect it may be playing a role in my problem). I'm trying to use it in my current Spring project which doesn't use Commons Config but uses the PropertyPlaceholderConfigurer. I've got the properties I need written out in a .properties file.
I've added the library as a dependency in my pom.xml file. I tried adding it into my web.xml like so
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
...
classpath:mylibrary-context.xml
</param-value>
</context-param>
When creating a bean in my Spring project, I tried to reference one of the beans defined in the library, so I could use it. There's an error creating one of the library beans that requires a property from my properties file.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'libraryBean' defined in class path resource [mylibrary-context.xml]: Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'java.lang.String' to required type 'boolean' for property 'useCompression'; nested exception is java.lang.IllegalArgumentException: Invalid boolean value [${foo.bar.use.compression}]
I know some of the other properties files in the project have boolean properties set the exact same way without issue. Here is how I'm loading the properties files in the applicationContext.xml(which after checking the logs, seems to load correctly)
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
...
<!-- loaded in order-->
<value>classpath:foo/bar/some-props.properties</value>
<value>classpath:foo/bar/some-monitoring.properties</value>
<value>file:/${user.home}/foo/bar/some-monitoring.properties</value>
<value>file:/etc/foo/bar/some-monitoring.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
and finally my some-monitoring.properties file:
foo.bar.use.compression=true
foo.bar.name=foobar
foo.bar.broker.url=someurl
So I'm just wondering what's going on with the boolean property and why? If its a general problem with the PropertyPlaceholderConfigurer not loading boolean properties, its odd that I'm not having a problem with the other properties files. Is it that there is some conflict because of the library using the Commons Configuration? My knowledge of how Spring loads up these properties is pretty shallow so this seems like a chance to get a better handle on what's going on under the covers.
If anyone can shed some light on what's going on and how to fix/get around it, any help is greatly appreciated!
Quoting Jurgen on the spring-dev list:
PropertyPlaceholderConfigurer is an implementation of the BeanFactoryPostProcessor interface: This interface and its sibling
BeanPostProcessor just apply to the BeanFactory that defines them,
that is, to the application context that defines them.
If you combine multiple config files into a single
contextConfigLocation, a PropertyPlaceholderConfigurer defined in any
of the files will apply to all of the files, because they are loaded
into a single application context.
As far as I understand, you tried to combine all your application contexts into the contextConfigLocation. According to the quotation, it should work fine. I think that you have other kind of problem. In my opinion both boolean and string properties are not loaded at all. Maybe it seems as string properties are loaded, but I think that their values are set to ${foo.bar.name} and ${foo.bar.broker.url}. Since they are Strings, they can hold these values. When Spring tries to set ${foo.bar.use.compression} to the boolean, you have typecast problem which just reveals wider issue.
Probably, if you set:
<property name="ignoreResourceNotFound" value="false"/>
<property name="ignoreUnresolvablePlaceholders" value="false"/>
you will see that PropertyPlaceHolderConfigurer cannot resolve your properties files.
Please also try using classpath*: notation when you refer to resources from other JARs.

How to declare a parent application context

I find myself using two identical beans in my applicationContext.xml and my applicationContext-test.xml. I'd like my test context to be able to inherit from my app context, to avoid repeating myself.
I've seen plenty of material indicating that you can declare a parent application context and reference beans from that context, but I can't find a useful example. Can anyone help?
Update
As some background info, my normal application context is being loaded in web.xml:
<context-param>
<description>Application Contexts for Spring</description>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
My test application context is loaded in my unit tests:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "/applicationContext-test.xml")
So let's say I have a bean in my regular context:
<bean name="someBean" class="com.foo.MyClass" />
Then, in my test application context, I'd like to refer to this bean. How do I do it?
Update
Per skaffman's suggestion, I've moved the bean into a SharedBeans.xml file and imported it into my applicationContext.xml. However, this causes a SAXParser exception:
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to import bean definitions from URL location [classpath:SharedBeans.xml]
Offending resource: ServletContext resource [/WEB-INF/classes/applicationContext.xml]; nested exception is org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 1 in XML document from class path resource [SharedBeans.xml] is invalid; nested exception is org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'bean'.
at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:68)
I can't be sure what I'm doing wrong. The bean was working fine in my context file, and all I did was cut and paste into the new file. Here are the contents of SharedBeans.xml in its entirety:
<bean name="properties" class="com.foo.Properties">
<constructor-arg><value>${module.name}</value></constructor-arg>
<constructor-arg><value>${businessUnit}</value></constructor-arg>
<constructor-arg><value>${product}</value></constructor-arg>
<constructor-arg><value>${env}</value></constructor-arg>
<constructor-arg><value>${machineName}</value></constructor-arg>
<constructor-arg><value>${collectionSet.company}</value></constructor-arg>
<constructor-arg><value>${route.tag}</value></constructor-arg>
<constructor-arg><value>${timeout}</value></constructor-arg>
</bean>
This doesn't strike me as a particularly good use-case for a parent context, which is useful mainly to set up a hierarchy (e.g. one root webapp context, multiple child servlet contexts).
For your situation, it's going to be simpler and easier to understand if you just extract the common bean definitions into a separate file, and then <import> them into each context file that needs it. You could do this with parent-child contexts, but it's going to be harder to understand, unnecessarily so.
OK, so an example, put your shared bean definition into a file called shared-beans.xml, and put it (for now) at the top-level of your classpath, containing:
<bean name="someBean" class="com.foo.MyClass" />
Then, inside applicationContext-test.xml and /WEB-INF/classes/applicationContext.xml, add the following:
<import resource="classpath:/shared-beans.xml" />
All of the bean definitions in shared-beans.xml will now be imported into each app context. You don't get a third app-context by doing this, you just import the bean definitions from another file.
You can move your common declaration to the separate XML file and place it in classpath (for easy access from test). Then you can do the following:
<context-param>
<description>Application Contexts for Spring</description>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/applicationContext.xml
classpath:/common-beans.xml
</param-value>
</context-param>
.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(
locations = {"/applicationContext-test.xml", "common-beans.xml"})
You can also include common-beans.xml from both contexts using <import>, as suggested by skaffman.

Resources