Is there a way to easily use Spring Injection to grab one or more *.xml data files from a folder location (either in a deployed *.war or on a server folder) and inject that data from the *.xml files into a Java class (e.g. in a web service)? I have been asked by another programmer if I can do this.
I've had a look at a few links on stackoverflow, but so far the easiest way I've found is to put the *.xml files into a particular folder location (e.g. WEB-INF/classes) and use something like this to retrieve them:
Thread.currentThread().getContextClassLoader.getResourceAsStream("/WEB-INF/classes/data.xml")
The above method is easy; however, it is obviously not Spring Injection. Is there a way to do this using Spring Injection instead? I would have thought that since configuration files can be loaded this way, that xml data could also be loaded similarly.
Thanks.
Spring provides a class called Resource which you can use to inject resource files into a spring bean. So you can do this:
public class Consumer {
public void setResource(Resource resource) {
DataInputStream resourceStream = new DataInputStream(resource.getInputStream());
// ... use the stream as usual
}
...
}
Then:
<bean class="Consumer">
<property name="resource" value="classpath:path/to/file.xml"/>
</bean>
or,
<bean class="Consumer">
<property name="resource" value="file:path/to/file.xml"/>
</bean>
You can also directly use the #Value annotation:
public class Consumer {
#Value("classpath:path/to/file.xml")
private Resource resource;
...
}
Related
I have a simple Spring bean
public class Widget {
public Widget(File rootDir) { ... }
}
and in my application context XML I want to create an instance of Widget:
<bean id="widget" class="com.example.Widget">
<constructor-arg type="java.io.File" value="classpath:/someDir"/>
</bean>
When I run in from my IDE it works, the string is converted to a File and passed to the ctor.
When I run it with mvn exec:java the file cannot be found, I get all sorts of errors, but revolving around:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name widget defined in class path
resource [META-INF/context.xml]: Could not resolve matching
constructor (hint: specify index/type/nam e arguments for simple
parameters to avoid type ambiguities)
So, how to I pass a classpath File (actually, directory) to a bean constructor in Spring?
I need a directory becasue then I want to scan it / list all files inside it.
So, how to I pass a classpath File (actually, directory) to a bean
constructor in Spring? I need a directory becasue then I want to scan
it / list all files inside it.
I don't know if that's possible but you can get all of the resources on the classpath that match a pattern using Spring. See PathMatchingResourcePatternResolver. See the docs on using Ant-style wildcards.
Try this for your constructor:
import org.springframework.core.io.Resource;
...
public Widget(Resource[] resources) { ... }
and in your XML, something like this (change the Ant pattern to match your use case):
<bean id="widget" class="com.example.Widget">
<constructor-arg>
<list>
<value>classpath:/someDir/*.foo</value>
</list>
</constructor-arg>
</bean>
Before using this, I recommend you take a look at the caveats related to non-filesystem resources mentioned in the PathMatchingResourcePatternResolver Javadocs.
I am a developer working on a Java web application that is built on the Spring framework.
This application will be deployed to several different customers.
There is a class which contains some business logic that is different for each client.
From a Spring framework point of view, it is enough to simply wire in the appropriate class (as a bean) for each client.
However, the developers are not responsible for deployment of the application. The Operations team is responsible, and having them open up the WAR file and modify the spring configuration XML for each client deployment is probably too much to ask from them. Properties files are ok, but modifying internal files - probably not.
Has anyone else come up with a strategy for dealing with this?
Edit:
To give an example of what I'm talking about:
public interface IEngine {
void makeNoise();
}
public class Car {
public void setEngine(IEngine engine) {
this.engine = engine;
}
}
Customer A's business logic:
HeavyDutyEngine implements IEngine {
public void makeNoise() {
System.out.println("VROOOM!");
}
}
Customer B's business logic:
LightWeightEngine implements IEngine {
public void makeNoise() {
System.out.println("putputput");
}
}
In the Spring configuration XML:
For client A, it might look like this:
<bean id="hdEngine" class="HeavyDutyEngine" />
<bean id="lwEngine" class="LightWeightEngine" />
<bean id="car" class="Car">
<property name="engine" ref="hdDngine">
</bean>
For client B, it might look like this:
<bean id="hdEngine" class="HeavyDutyEngine" />
<bean id="lwEngine" class="LightWeightEngine" />
<bean id="car" class="Car">
<property name="engine" ref="lwEngine">
</bean>
To configure Spring for different environments, you can use the concept called spring "profiles". (introduced in Spring 3.1)
You have different ways so enable/disable this properties. For example java properties parameter.
But because you are using a WAR, and therefore some Servlet container, I would recommend to put this configuration in the Servlet container.
In a tomcat for example, you can put this line in the context.xml (global or application specific) to enable a profile
<Parameter name="spring.profiles.active" value="myProfile"/>
You can move some configuration to DB, if you are using one.
And you can use something like this
ref="{ref-name}"
where ref-name can be resolved using properties file(default) by configuring PropertyPlaceholderConfigurer.
Or you can write your own wrapper over PropertyPlaceholderConfigurer which will take the values from DB table which is external to you deployable WAR file.
In one of mine project, we used this method to resolve custom dependencies. The wrapper which looks up the DB used to take first priority and if the DB doesn't have the key/value pair, then properties file (bundled in the WAR) was used to resolve dependencies.
This will also allow you to change some value externally from DB, however with IBM Websphere we need to recycle the server for changes to take place.
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.
I want to inject a map containing all the properties that spring knows of (which are inserted by a library) to a config class that I have through the spring xml. Is that possible?
<bean class="Config">
<constructor-arg name="env">
<map>
//inject all properties?
</map>
</constructor-arg>
</bean>
Why don't you just inject the Spring Context? Through the Context, you can look up any bean via its name.
Edit:
From this answer, you could also use the following:
<bean class="Config">
<constructor-arg name="env">
<util:properties location="${path.to.properties.file}"/>
</constructor-arg>
</bean>
Where your "env" constructor argument is a java.util.Properties object.
For later versions of Spring (including spring-boot) that support the injection of an Environment you can use this to access all properties loaded.
To answer this question inject a AbstractEnvironment so that you are able to call the getPropertySources() method that will allow you to see where the properties have been loaded from (e.g. a file, OS variables, etc)
#Autowired
public Config(AbstractEnvironment environment)
{
MutablePropertySources propertySources = environment.getPropertySources();
// inspect propertySources to see all properties loaded by Spring
}
Can you not extend the library class that you use and instantiate your bean instead of the default library one? Then you would be able to inspect all the values.
Otherwise, if you know the signature of the library, you can always use AOP to weave some code around the library and get access to the properties there. A bit more complicated, but still gets you where you need to go. You can definitely use AspectJ (which requires a little more config) or even Spring AOP, depending how things are being accessed.
If you want/need more insight on this, let me know.
I have a bean that has a property of type File. I want that property to end up pointing to a file under WEB-INF.
It seems to me that the ServletContextResourceLoader should have that job, somehow, but nothing I try seems to do the job.
I'm trying to avoid resorting to something like this in the Java code.
If that property has to remain as type "File", then you're going to have to jump through some hoops.
It would be better, if possible, to refactor the bean to have a Resource property, in which case you can inject the resource path as a String, and Spring will construct a ServletContextResource for you. You can then obtain the File from that using the Resource.getFile() method.
public class MyBean {
private File file;
public void setResource(Resource resource) {
this.file = resource.getFile();
}
}
<bean class="MyBean">
<property name="resource" value="/WEB-INF/myfile">
</bean>