What I would like to achieve is the ability to "dynamically" (i.e. based on a property defined in a configuration file) enable/disable the importing of a child Spring XML context.
I imagine something like:
<import condition="some.property.name" resource="some-context.xml"/>
Where the property is resolved (to a boolean) and when true the context is imported, otherwise it isn't.
Some of my research so far:
Writing a custom NamespaceHandler (and related classes) so I can register my own custom element in my own namespace. For example: <myns:import condition="some.property.name" resource="some-context.xml"/>
The problem with this approach is that I do not want to replicate the entire resource importing logic from Spring and it isn't obvious to me what I need to delegate to to do this.
Overriding DefaultBeanDefinitionDocumentReader to extend the behaviour of the "import" element parsing and interpretation (which happens there in the importBeanDefinitionResource method). However I'm not sure where I can register this extension.
Prior to Spring 4, the closest you can get using standard Spring components is:
<import resource="Whatever-${yyzzy}.xml"/>
where ${xyzzy} interpolates a property from the system properties. (I use a hacky custom version of the context loader class that adds properties from other places to the system properties object before starting the loading process.)
But you can also get away with importing lots of unnecessary stuff ... and use various tricks to only cause the necessary beans to be instantiated. These tricks include:
placeholder and property substitution
selecting different beans using the new Spring expression language,
bean aliases with placeholders in the target name,
lazy bean initialization, and
smart bean factories.
This is now completely possible, using Spring 4.
In your main application content file
<bean class="com.example.MyConditionalConfiguration"/>
And the MyConditionalConfiguration looks like
#Configuration
#Conditional(MyConditionalConfiguration.Condition.class)
#ImportResource("/com/example/context-fragment.xml")
public class MyConditionalConfiguration {
static class Condition implements ConfigurationCondition {
#Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.PARSE_CONFIGURATION;
}
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// only load context-fragment.xml if the system property is defined
return System.getProperty("com.example.context-fragment") != null;
}
}
}
And then finally, you put the bean definitions you want included in the /com/example/context-fragment.xml
See the JavaDoc for #Conditional
As mentioned earlier, this can be easily accomplished with profiles if you're using Spring 3.1+
<!-- default configuration - will be loaded if no profile is specified -->
<!-- This will only work if it's put at the end of the configuration file -->
<!-- so no bean definitions after that -->
<beans profile="default">
<import resource="classpath:default.xml" />
</beans>
<!-- some other profile -->
<beans profile="otherProfile">
<import resource="classpath:other-profile.xml" />
</beans>
otherProfile can be easily activated with e.g.
mvn install -Dspring.profiles.active=otherProfile
if you're using different profiles in tests, just add -DforkMode=never to make sure that the tests will run inside same VM, therefore the param spring.profiles.active wont be lost
With Spring 3.1.x you can use bean profiles to achieve conditional resource import and bean instantiation. This is of course of no help if you are using an earlier version :)
For the record, Robert Maldon explains how to accomplish conditional definition of beans in this post: http://robertmaldon.blogspot.com/2007/04/conditionally-defining-spring-beans.html. It is a bit long to copy it here (besides, I don't think I should copy-paste his article anyway).
The end result with this approach, adapted for your example, is:
<condbean:cond test="${some.property.name}">
<import resource="some-context.xml"/>
</condbean:cond>
It is certainly not so simple as Stephen C's solution, but it is much more poweful.
Another one to consider for Spring 3.0:
<alias name="Whatever" alias=""Whatever-${yyzzy}" />
where ${xyzzy} interpolates a property from the system properties.
Another option is to have your app load a modules-config.xml file that is located in the /conf folder and edit it during the install/config phase to uncomment the modules you want loaded.
This is the solution I'm using with a web application that serves as a container for different integration modules. The web application is distributed with all the different integration modules. A modules-config.xml is placed in tomcat's /conf folder and the conf folder is added to the classpath (via catalina.properties/common.loader property). My web app webapp-config.xml has a <import resource="classpath:/modules-config.xml"/> to get it loaded.
You can override contextInitialized(javax.servlet.ServletContextEvent event) in your own ContextLoaderListener and set required System property before super.contextInitialized(event) called like this
package com.mypackage;
import org.springframework.web.context.ContextLoaderListener;
public class MyContextLoaderListener extends ContextLoaderListener {
public void contextInitialized(javax.servlet.ServletContextEvent event) {
System.setProperty("xyz", "import-file-name.xml");
super.contextInitialized(event);
}
}
And than replace ContextLoaderListener to MyContextLoaderListener in your web.xml
<listener>
<listener-class>com.mypackage.MyContextLoaderListener</listener-class>
</listener>
Now you can use in your spring.xml
<import resource="${xyz}" />
I hope this will help.
Related
I have a Spring 3.1 application. Let's say it has an XML with the following content:
<context:property-placeholder location="classpath:somename.properties" />
<context:property-placeholder location="classpath:xxx.properties" />
I would like some.properties to be always loaded (let's assume it exists), but the xxx part of the second place holder to be replaced by some name depending on the active profile. I've tried with this:
<beans profile="xx1">
<context:property-placeholder location="classpath:xx1.properties" />
</beans>
<beans profile="xx2">
<context:property-placeholder location="classpath:xx2.properties" />
</beans>
Also, both files have properties with the same key but different value.
But it didn't work as some later bean that has a placeholder for one property whose key is defined in xx1.properties (and xx2.properties) makes Spring complain that the key is not found in the application context.
You can do:
<context:property-placeholder location="classpath:${spring.profiles.active}.properties" />
It works fine, but is perhaps not adapted when using multiple profiles in the same time.
When declaring 2 property placeholders, if the 1st one does not contain all the applications keys, you should put the attribute ignoring unresolvable = true, so that the 2nd placeholder can be used.
I'm not sure if it is what you want to do, it may if you want both xx1 and xx2 profiles be active in the same time.
Note that declaring 2 propertyplaceholders like that make them independant, and in the declaration of xx2.properties, you can't reuse the values of xx1.properties.
If you need something more advanced, you can register your PropertySources on application startup.
web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.xxx.core.spring.properties.PropertySourcesApplicationContextInitializer</param-value>
</context-param>
file you create:
public class PropertySourcesApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertySourcesApplicationContextInitializer.class);
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
LOGGER.info("Adding some additional property sources");
String profile = System.getProperty("spring.profiles.active");
// ... Add property sources according to selected spring profile
// (note there already are some property sources registered, system properties etc)
applicationContext.getEnvironment().getPropertySources().addLast(myPropertySource);
}
}
Once you've done it you just need to add in your context:
<context:property-placeholder/>
Imho it's the best way to deal with spring properties, because you do not declare local properties everywhere anymore, you have a programmatic control of what is happening, and property source xx1 values can be used in xx2.properties.
At work we are using it and it works nicely. We register 3 additional property sources:
- Infrastructure: provided by Puppet
- Profile: a different property loaded according to the profile.
- Common: contains values as default, when all profiles share the same value etc...
I have decided to submit and answer to this as it has not yet been accepted. It may not be what you are looking for specifically but it works for me. Also note that i am using the new annotation driven configuration however it can be ported to the xml config.
I have a properties file for each environment(dev.properties, test.properties etc)
I then have a RootConfig class that is the class that is used for all the configuration. All that this class has in it is two annotations: #Configuration and #ComponentScan(basePackageClasses=RootConfig.class).
This tells it to scan for anything in the same package as it.
There is then a Configuration Containing all my normal configuration sitting wherever. There is also a configuration for each environment in the same package as the root configuration class above.
The environment specific configurations are simply marker classes that have the following annotations to point it to the environment specific properties files:
#Configuration
#PropertySource("classpath:dev.properties")
#Import(NormalConfig.class)
#Profile("dev")
The import tells it to bring in the normal config class. But when it gets in there it will have the environment specific properties set.
We are using spring's component-scan and don't want application context to load test beans (even if they are defined in the same path i.e a.b.c).
MyPackage
src
a.b.c.SRC
tst
a.b.c.TST
I have read that order would be to load src folder first and then the test folder. In above case, if I component-scan a.b.c, I only want to load beans from SRC. How is that possible?
You can use the #Profile("ProfileName") annotation on your components and of course setting the active profile on execution. The Component Scan will ignore beans that dont match the active profile. e.g.
#Component
#Profile("production")
public class productionImpl implements SomeInterface{}
#Component
#Profile("test")
public class testImpl implements SomeInterface{}
All you have to do after that is setting the correct profile in live execution. You can set it either as a JVM argument (spring.profiles.active) or set it to the applicationContext: ApplicationContext.getEnvironment().setActiveProfiles(“ProfileName”);
For your test execution classes you can use #ActiveProfiles("TestProfileName")
You can check this site for a more detailed example: http://websystique.com/spring/spring-profile-example/
There are several exclude filters for classpath scanning you can define in your context configuration file. aspectj filter type seems to be the one you would want to use.
<context:component-scan base-package="a.b.c">
<context:exclude-filter type="aspectj" expression="a.b.c.TST.*" />
</context:component-scan>
Alternatively, if you would like to have a bit more granularity, you can define your own annotation and use the annotation exclude-filter type.
<context:component-scan base-package="com.example">
<context:exclude-filter type="annotation" expression="path.to.your.package.ScanExclude"/>
</context:component-scan>
This way, all classes annotated with #ScanExclude annotation are effectively ignored
Using Spring Dependency Injection we can remove the hard-coded dependencies from our code and dependences can be resolved at run-time instead of compile-time.
In few years ago, we were working in a project which was implemented for two countries. Almost all the modules were same
apart from one module -tax calculation.
The plan was instead of packaging twice for two different countries, we will package the project only once for both the countries.
So we had one TaxCalculationService interface with one method calculateTax(...)
Two different TaxCalculationServiceImpl classes for two countries which implements TaxCalculationService interface
Just like below
TaxCalculationServiceImplForCountryA implements TaxCalculationService
TaxCalculationServiceImplForCountryB implements TaxCalculationService
We called the calculateTax(...) method from our manager class(TaxCalculationManagerImpl).
And we had spring configuration file which is kept in an folder out side the war let say /home/config/
Both the war deployed into two different server.Same war, packaged once, only difference is in spring configuration file.
<!-- For country A-->
<bean id="taxCalculationService" class="com.TaxCalculationServiceImplForCountryA"/>
<bean id="taxCalculationManager" class="com.TaxCalculationManagerImpl">
<property name="taxCalculationService" ref="taxCalculationService"/>
</bean>
For Country B we need to modify the class name with bean id "taxCalculationService", nothing else
<!-- For country B-->
<bean id="taxCalculationService" class="com.TaxCalculationServiceImplForCountryB"/>
<bean id="taxCalculationManager" class="com.TaxCalculationManagerImpl">
<property name="taxCalculationService" ref="taxCalculationService"/>
</bean>
So without doing recompile, packaging we easily used the same application war for both the countries, only changed the spring configuration file.
But in Spring - Java Based Configuration, if we want to implement same scenario, how we are going to do that?
While we are creating a bean we are using new operator to create a specific implementation class.
If we use the same interface TaxCalculationService and TaxCalculationServiceImplForCountryA class for country A then we have to something like
#Bean
public TaxCalculationService getTaxCalculationService(){
return new TaxCalculationServiceImplForCountryA();
}
For country B we need to do something like this
#Bean
public TaxCalculationService getTaxCalculationService(){
return new TaxCalculationServiceImplForCountryB();
}
So in this case we need to use new operator to create a specific implementation class of TaxCalculationService for a specific country. So what ever the flexibility we had in our xml based spring configuration, with out re-compiling, packaging the war, we reused the war, that can not be achieve in Java Based Configuration approach.
taxCalculationManager is dependent on taxCalculationService and whether taxCalculationManager is going invoke the calculateTax(...) of TaxCalculationServiceImplForCountryA or TaxCalculationServiceImplForCountryB that will be decided at run-time in xml based spring configuration.
But when we have to specify in the java code that the getTaxCalculationService() will return new TaxCalculationServiceImplForCountryA() or new TaxCalculationServiceImplForCountryB() then it is basically not resolving at the run-time.
So the dependency of taxCalculationManager on taxCalculationService is not resolving at run-time, it is basically resolving at compile time.
So don't you think in Spring - Java Based Configuration the beauty of Spring DI -resolving dependences at run-time instead of compile-time has lost?
One possible option is to use Spring Profiles (#Profile). Based on profile you configured in configuration file or pass into project via system property, you can activate or deactivate beans.
What is the best approach for creating services that load a property set at runtime (bean is passed "xyz" and loads xyz.properties)? These properties files need to be able to be dropped into a folder outside the classpath before a command is entered to start the service (edit: this could happen at any time while the program is running).
I already have a system to do this that we've been using for over a year, but I'm migrating to spring to make the code more modular (customize services more easily through DI) and easier to maintain. My current method of creating an environment and then passing it with "this" to the dependencies just seems upside down from an IoC standpoint.
Is there a way to use a PropertyPlaceholderConfigurer without hardcoding the name of the property file? Maybe just a reference to a variable I pass into the constructor of the service that its dependencies can load? So far, it looks like I will have to create a service and inject its dependencies without any config and then call a separate load method for each to pass in the properties, but that just seems like I'm not really using spring.
USE CASE: The app will pool client connections to various servers and will forward requests from other applications to these servers. New profiles must be able to be added by non-programmers without taking down or restarting the app. The profiles will include basic things like host, port, and login info, but also more complex things like whether to use tcp/http, ssl/https (which will determine which client type to use), and timeouts and pool min/max/etc (which will need default values).
I tried with PropertyPlaceholderConfigurer and frankly, I couldn't wrap my head around it, somehow. It's easy enough to use when you use the existing options but I couldn't extend the framework.
So my approach was much more simple:
Create an annotation #InjectConfig which takes a config key as parameter.
In your beans/services, annotate fields or public setters with this annotation.
Write a BeanPostProcessor which takes options from a "config provider" and injects them into the fields / setters.
Now all you need is a config provider. Inject that into the post processor and you're done.
Note: I prefer annotating setters because that means you can easily configure your services from tests (just call the setters) without having to come up with smart names for 238576 config files.
EDIT If you have many configs, then a config factory might be a better choice:
Create a key to describe a config bundle (I usually use an enum or a new type here to prevent typos)
Put this key into the service when you create it (manually or via Spring)
Write a config factory that can return Properties or a Map for a config key.
Inject this factory into your service
In the init code of your service, use the key to lookup your config via the factory.
Using this approach, you can have a dummy factory that always returns the same thing in tests and a more complex factory for production.
The real factory can then be configured via spring so it knows where to look for configuration files. One approach is to register a java.io.File per config key. Now your concerns (configuring a service and loading configs) are completely separated.
PropertyPlaceholderConfigurer reads and initialize files on application context initialization and only once. So most probably you cannot configure it at runtime.
But you can have variables. For example, for my case I have default properties and user specific properties. So PropertyPlaceholderConfigurer loads properties from classpath first and after that is trying to find additional properties at defined location (user home folder). I user's property file exists so configurer loads it and override properties.
Here is my example:
<bean id="config" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true"/> <!-- do not throw exception if file not found -->
<property name="locations">
<list>
<value>classpath:server.properties</value>
<value>file:${user.home}/user.properties</value>
</list>
</property>
</bean>
I'm not sure that this answer is what you're exactly need. But I'm trying to guess what is your actual task. So if you need re-read properties runtime each time you access them you have to do it manually like you did before because spring application context helps you configure your application initial configuration.
It seems like the best approach may be to use a ServiceManager that contains the main ApplicationContext and then have each Service initialize its own FileSystemXmlApplicationContext with the main context as the parent like so:
public class ServiceManager {
ApplicationContext appContext;
String APP_HOME = System.getProperty("user.home") + File.separator;
public void init() {
//set main spring context
appContext = new AnnotationConfigApplicationContext(AppConfig.class);
}
public void start(String serviceName) throws Exception {
ApplicationContext serviceContext = new FileSystemXmlApplicationContext(
new String[]{APP_HOME + serviceName + ".xml"}, //path to child ctx
appContext); //reference to parent ctx to build hierarchy
Service service = (Service) serviceContext.getBean("service");
service.start();
}
}
The ApplicationContext is a bit heavy to be duplicating, but memory is pretty cheap these days and this provides total separation of concerns. I still have shared logging and an event system managed by the parent context, and each service is now simplified in its own config. I built a proof of concept using two services, and it seems to work fine so far. I'll add another comment once I finish the other services and finish testing.
reference:
http://techo-ecco.com/blog/spring-application-context-hierarchy-and-contextsingletonbeanfactorylocator/
I have a main app-context.xml that defines a property placeholder with two locations: default properties file and an optional override file:
<context:property-placeholder
location="classpath:config.properties,${configOverride}"
ignore-resource-not-found="true" />
The optional override location allows specifying another properties file (e.g. "-DconfigOverride=file:/home/app/config.properties") with only the properties that should be overridden.
For my unit tests, I'm using a test context that imports app-context.xml:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:test-context.xml"})
public class UserServiceTest {
...
}
How can I set system properties or environment variables within the application before the application context is loaded? I would like to achieve the same effect as setting "-DconfigOverride=classpath:testConfig.properties" across all test classes without having to specify a command line arg, if possible.
One other alternative is setting the environment property in a #BeforeClass annotated method, which will be invoked before the Context Configuration happens.
#BeforeClass
public static void setSystemProps() {
System.setProperty("configOverride", "yourVal");
}
Thinking of ,
Extending SpringJUnit4ClassRunner and setting the system property
configOverride in its constructor/initialization block
Then passing ExtendedSpringJUnit4ClassRunner to #RunWith
Here's what I ended up doing - I didn't have to change any unit test classes. Unfortunately, I didn't set the "configOverride" property (see AhamedMustafaM's answer for one way to do that) and instead went with overriding the original property placeholder definition (I tried again after my initial failed attempts and got it to work).
I added the following line to my testContext.xml:
<!-- import the main app context -->
<import resource="classpath:appContext.xml" />
<!-- this is the line i added -->
<context:property-placeholder order="-999"
location="classpath:testConfig.properties"
ignore-unresolvable="true" />
Note the order="-999" attribute, which is used to ensure priority over the original property-placeholder definition(s). Also I set "ignore-unresolvable" to "true" to delegate any unresolvable properties to the original placeholder configurer.
My issue was similar but I wanted to set the spring.profiles.active environment variable and it turned out that I just needed to throw #ActiveProfiles() with the values I wanted on to the test itself.