Can SpEL be used with import statements in spring xml configuration - spring

I am trying to use SpEL in import statement in spring xml configuration.
I am trying to do this
<import resource="classpath*:/'#{'${enable.mass.quote.service:false}'=='true' ? 'massquoting' : 'quoting'}'-beans.xml"/>
but it is not working, any suggestions please

Actually you are right: <import resource> doesn't support SpEL, but it supports property-placeholders:
// Resolve system properties: e.g. "${user.dir}"
location = environment.resolveRequiredPlaceholders(location);
So, for your case it might look like:
<import resource="classpath*:/${enable.mass.quote.service:quoting}-beans.xml"/>
where enable.mass.quote.service should have value massquoting.
If you have more high condition, e.g. enable.mass.quote.service=true, you should rely on Spring Profiles:
<beans profile="service">
<import resource="classpath*:/massquoting-beans.xml"/>
</beans>
<beans profile="nonService">
<import resource="classpath*:/quoting-beans.xml"/>
</beans>

I too came across this requirement and turns out the answer is NO. SpEL or placeholders cannot be used in Spring import statements because the import happens before the placeholders/expressions are evaluated and substituted. Refer here. Spring profiles is the answer just that Spring profiles were floated beginning Spring 3.1.x so when using an older version of Spring one might have to take on the onerous task of writing custom namespaces and their handlers.

Related

Spring schema folder & Spring XML namespaces

I am looking for how we should determine the symbol of a namespace in Spring bean XML definitions. I guess they are in the Spring schema folder, but I can't find it. For example, what are c:, p:, util:, .. in the XML bean configuration?
Where can I find the schema's for each namespace? For example, how do I know if I should use http://www.springframework.org/schema/p in xmlns:p="http://www.springframework.org/schema/p", where are the other namespaces and how can I find them?
You can choose the symbol (p, util, jee, beans, ...) by yourself. These are namespaces, and they work by adding the xmlns attribute like:
<beans xmlns:util="http://www.springframework.org/schema/util">
<!-- Content -->
</beans>
In this case we said that util: will be used by the util schema, so you'll have to use <util:properties> to access things from this namespace. But you could also have said xmlns:foobar="http://www.springframework.org/schema/util" in which case you could have used things like <foobar:properties>.
But you also need to provide the location of the XSD of that namespace by using xsi:schemaLocation:
<beans xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- Content -->
</beans>
In this case the XSD for http://www.springframework.org/schema/util is available at http://www.springframework.org/schema/util/spring-util.xsd. The http://www.springframework.org/schema/util part is just a label and can be chosen as well. The only thing that has to match is the XSD schema.
For more information about XML namespaces, you should look at this question and its answers.
A list of common XML schema's with Spring can be found in their documentation (33. XML Schema-based configuration). However, these only list the core schemas. Some projects (like Spring Web Services, ...) have their own namespaces like:
http://www.springframework.org/schema/web-services/web-services.xsd
http://www.springframework.org/schema/oxm/spring-oxm.xsd
...
You can find the entire list by visiting the Index of /schema. However, like I mentioned, most of these are only used for specific Spring projects, don't just import them, read the specific documentation to find out what you need. The documentation about the constructor namespace (c:) can be found in 7. The IoC container.

How does the Spring framework process XML namespace based syntax for configuring spring components?

While reading about spring-rabbit, I stumble upon the following syntax of configuring spring components:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<rabbit:connection-factory id="connectionFactory"/>
<rabbit:template id="amqpTemplate" connection-factory="connectionFactory"/>
<rabbit:admin connection-factory="connectionFactory"/>
<rabbit:queue name="myqueue"/>
</beans>
I am only aware of bean way of configuring components, where you declare a bean with id and class and set appropriate properties. I am not clear about the above way, by making use of XML namespace, of configuring components and how it is internally handled by Spring.
Thanks.
The use of namespaces is really nothing more than shortcut configurations. Behind the scenes, Spring is doing all of your bean creating like you mentioned you are accustomed to doing yourself. As for HOW, well, there are a few components. First you need a registered NamespaceHandler. Going with your spring-rabbit example, you'll find in the spring-rabbit jar a file at a META-INF/spring/spring.handlers with something like this:
http\://www.springframework.org/schema/rabbit=org.springframework.amqp.rabbit.config.RabbitNamespaceHandler
This is registering RabbitNamespaceHandler for the rabbit namespace configurations. Alongside that file you will see one called spring.schemas, and its purpose is to register the schemas that constrain the rabbit namespace.
The next step is to have one or more BeanDefinitionParser instances that do the "magic" of turning your shortcut configurations into full-blown bean creation definitions, so to speak. In this rabbit example, you will find ConnectionFactoryParser which is responsible for parsing the <rabbit:connection-factory /> element. It simply looks at all of the attributes, sub-elements, etc. that your declare in xml and creates the necessary beans for you.
You can follow this formula to create your own namespace configuration as well.
Spring's XML namespace support provides a way to register several components at once, in a more succinct and meta-data driven (or indeed domain-specific) approach. Anything you can do with a namespace handler, could also be done with (probably quite a lot more) bean definitions.
Creating a namespace handler is a fairly simple process - and Spring, by its extensible nature easily allows you to create your own, if you wish.
Here's a some detailed documentation on how to do that:
http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/apbs03.html
http://cscarioni.blogspot.com/2012/04/creating-custom-spring-3-xml-namespace.html

#ActiveProfile and spring.profiles.active

This is a piece of my applicationContext definition to retrieve some properties.
<!-- get some properties -->
<context:property-placeholder
ignore-resource-not-found="false" ignore-unresolvable="false"
location="classpath:/properties/${spring.profiles.active:test}/some.properties"/>
As you can see I letting the spring.profiles.active decide which properties will be read.
My tests are annotated with:
#ActiveProfile("integration")
You guessed it right my spring bean profiles are actually matching the environments in which to deploy/test the application.
Still my location property is getting resolved to "/properties/test/some.properties". Which is of course because the spring.profiles.active doesn't seem to get resolved in this case.
How could I achieve getting the the right properties?
It is because active profiles may be activated by system property (but in case of #ActiveProfiles it works another way).
Just like this:
<beans profile="dev,prod,qa">
<context:property-placeholder location="classpath:some.properties" ignore-unresolvable="true"/>
</beans>
<beans profile="test">
<context:property-placeholder location="classpath:some-test.properties" ignore-unresolvable="true"/>
</beans>
Also, you may try to change
location="classpath:/properties/${spring.profiles.active:test}/some.properties"
to
location="classpath:/properties/${spring.profiles.active}/some.properties"
See ticket: https://jira.springsource.org/browse/SPR-8982#comment-88498
Someone already had made a request for this:
An option to override an #ActiveProfile specified by test in runtime from command line by "-Dspring.profiles.active" or other systemProperty
My comment:
That or it should set the property spring.profiles.active.

How to retrieve context parameters in Spring 3.1 xml context

It seems like there's been a few iterations of property support in spring it's hard to tell what's best practice and the manuals are written from the point of view of someone who is familiar with every other iteration. I feel like this should be a simple and common requirement but given how hard it's been please correct me if there's a more idiomatic way.
What I want is to pass an additional properties file to my spring web app based on a context property which the client is setting using a tomcat descriptor like so
<Context path="/foo" reloadable="true">
<Parameter name="foo.config" value="file:${catalina.base}/conf/foo.properties"/>
</Context>
In spring for the live profile I have this
<beans profile="live">
<context:property-placeholder location="classpath:timetabling.live.properties,${timetabling.config}"
ignore-resource-not-found="true" />
</beans>
So I'd assumed this doesn;t work because I'm trying to configure placeholder suppport with a placeholder. If I use a system property however then this works fine. I know that spring 3.1 has baked in support for system and environment properties so I guess my question is how can I augment this support with something context aware before the placeholder is resolved?
--Update--
looking at http://blog.springsource.org/2011/02/15/spring-3-1-m1-unified-property-management/ particularly at footnote 1, I would expect to have a DefaultWebEnvironment which should already have aceess to context init params. Now I am more confused, can someone provide me with a concrete example of context property retrieval? At this point I feel like I've read every javadoc available and they are just not helpful.
<context:property-placeholder /> sets up a PropertyPlaceholderConfigurer which reads from .properties, system properties and environment variables. A Tomcat context.xml however sets up a servlet context init parameter. So what you need is a ServletContextPropertyPlaceholderConfigurer.

Switching spring application context or Impl java classes using maven profiles

I know the question I'm asking must be answered somewhere but I cant find an example on the net with some guidelines.
I wish to swap out the Spring 3.0 bean definition at build and runtime for a common codebase. I'm also using Maven profiles to build different versions of the same code.
Currently my bean def is
<bean id="somebean" class="com.x.SomeImpl" >
I will need to replace and deploy it at some times as
<bean id="somebean" class="com.x.SomeOtherImpl" >
Now the approaches I'm thinking of are
1) Use Maven profiles to switch out the complete applicationContext.xml to some other applicationContextB.xml based on the Maven profile.
2) Use Maven profiles to somehow? replace only the bean id definition for "somebean"
My questions are:
a) How can Option 2 be achieved?
b) These approaches still compile and package both SomeImpl and SomeOtherImpl during build. How can I pick only one and not the other for compliation and packaging into EAR?
I know Spring 3.1 has env profiles for the beans, but presently that's not an option.
You could consider using the maven resources and filtering feature available. You could have placeholders in your context file and a property file per profile holding values. In each profile, you could use a different property file and appropriately have your context file filtered.
Take a look to Maven Build Helper Plugin add-source.
You can combine profiles and Build Helper Plugin (add-source goal) to add the required classes in both cases.
you can import multiple config files to your config like this:
<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-2.5.xsd">
<import resource="applicationContextA.xml"/>
<import resource="applicationContextB"/>
</beans>
For switching you can use Spring profiles.

Resources