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>
Related
I am attempting to use SpEL to get the name of the bean currently being instantiated to allow multiple beans of same class to be created with different properties supplied by #PropertySource. I am hoping for something like the following:
public class SampleBean {
#Value("${#{CurrentBeanName}.val}")
private String val
}
Other bean:
public class OtherBean {
#Autowired
#Qualifier(name="BeanA")
SampleBean beanA;
#Autowired
#Qualifier(name="BeanB")
SampleBean beanB;
}
properties file:
BeanA.val=VALUE A
BeanB.val=VALUE B
If I add beanName=BeanA to my properties file, I am able to get this to work with
#Value("${${beanName}.val}")
Any ideas on what to do for #{BeanName}? If this is impossible then so be it, but if it works it would be much cleaner than my current solution.
EDIT:
Or any way to pass a constant from the xml bean definition to SpEL? example:
<bean id="BeanA" class="...">
<property name="prefix" value="BeanA"/>
</bean>
java:
public class SampleBean {
#Value("${#{prefix}.val}")
private String val
}
Any sort of attribute or anything would work
EDIT2:
This is trivial in old XML based config
spring.xml:
<bean id="beanA" class="SampleBean">
<property name="val" value="${BeanA.val}"/>
</bean>
<bean id="beanB" class="SampleBean">
<property name="val" value="${BeanB.val}"/>
</bean>
SampleBean.java:
public class SampleBean {
private String val;
public void setVal (String val) {
this.val = val;
}
}
However when switching to the new #Value annotations to get rid of all the setters, it seems non-singletons with diff properties aren't supported (i.e. no way to dynamically filter #Value arguments on bean creation)
No; it is not possible to reference the current bean.
EDIT
To address your comment below, the Java Configuration equivalent of
<bean id="BeanA" class="com.my.Foo">
<property name="prefix" value="BeanA"/>
</bean>
is
#Bean
public Foo BeanA() {
Foo a = new Foo();
a.setPrefix("BeanA");
}
although, by convention, you'd probably name it beanA.
If you have singleton bean types you could just use a static final variable for the name and then reference that. But the bigger issue is that you will be breaking the Spring inversion of control principals if you begin depending on Spring bean names, which is why this sort of thing isn't done. Pretty much want to focus on creating modules and domains for your project. If you begin accessing components coming from the Spring Context directly (such as the bean name) you will find that your modules will become brittle, hard to change and very hard to reason about as they begin to depend on behaviour from seemingly unrelated modules, such as the Spring Dependency Injection Framework. Although you may have a valid use-case for doing this you just need to be very very careful.
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.
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;
...
}
<context:property-placeholder
location="a.properties,b.properties"
ignore-unresolvable="true"/>
result: both properties file are loaded
<context:property-placeholder
location="${properties_location}"
ignore-unresolvable="true"/>
where properties_location is "a.properties,b.properties"
result: Exception in thread "main" org.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.io.FileNotFoundException: class path resource [a.properties,b.properties] cannot be opened because it does not exist
edit: ${properties_location} is set the following way:
System.getProperties().setProperty("properties_location", "a.properties,b.properties");
ApplicationContext ctx = new GenericXmlApplicationContext("context.xml");
...
How can I initialize my application the 2nd way? to have all the properties file's path defined in a placeholder.
You have to change this to:
<context:property-placeholder
location="classpath:a.properties,
classpath:b.properties"
ignore-unresolvable="true"/>
From the source of the parser for the property-placeholder element.
String location = element.getAttribute("location");
if (StringUtils.hasLength(location)) {
String[] locations = StringUtils.commaDelimitedListToStringArray(location);
builder.addPropertyValue("locations", locations);
}
First the location is retrieved, if that has a value it is converted to a String[]. Springs conversion service takes care of replacing any placeholders in the String[]. But at that moment the properties_location placeholder is just a single element in the array and that gets resolved to a.properties,b.properties without further processing.
So at the moment this isn't possible with placeholders I'm afraid.
One thing that might work is using SpEL if it is always going to be a system property you can use #{systemProperties['properties_location']} to resolve the value. That should be resolved before anything else.
You cant use a property placeholder as a value in a placeholder placeholder resolver. Its like saying, "hey, resolve the placeholder for the location of the all the properties, and then you can start resolving properties!".
Logically it just dosent make sense. I was experimenting with spring property placeholder resolution recently, and stumbled upon this same question. I attempted to use two property placeholder configurers, one to resolve the location of the properties for the second, and the second to resolve the rest of the properties. Of course this dosent work due to the way in which spring initialises its beans.
Initialise bean post processors
Construct them
Construct all other beans
Since the property placeholder configurer is a bean post processor, if you have more than one of them, they get initialised and constructed at the same time, so know nothing of each others properties at construction
Edit
Given that the property location is a system property you could have:
System.getProperties().setProperty("properties_location_a", "classpath:/a.properties");
System.getProperties().setProperty("properties_location_b", "classpath:/b.properties");
And then in your spring content.xml:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>${properties_location_a}</value>
<value>${properties_location_b}</value>
</list>
</property>
</bean>
I have a class that must be initialized with an absolute pathname. The thing I want to initialize it with the pathname of is a file sitting in WEB-INF.
I am using the ContextLoaderListener from Spring to set this all into motion, so I can't run Java code to obtain the path from the context and stick it where a ${whatever} could find it.
Consider some bean definition like:
<bean class="my.class">
<property name="somePath" value="/WEB-INF/a.txt"/>
</bean>
I need a way, if possible, to make that pathname pass through the ServletContextResource mechanism. There doesn't seem to be a 'prefix' for those like classpath:
In this case, it won't help to have the item in the classpath, trust me.
EDIT:
I went and dug up the source of the bean class, and it already accepts Resources on the relevant properties. So Something Ridiculous is going on here, insofar as it complains as if it can't find things. Off to the debugger for me.
EDIT AGAIN:
So, this turns out to be a maven prank, unrelated to Spring. It's upvotes all around for your help, and open another question.
My preference would be to modify this class to take a Resource, rather than a pathname. You can then inject it using:
<property name="fileResource" value="/WEB-INF/path/to/file"/>
This is more flexible, and you can use the various methods on the Resource interface to get things like the underlying file pathname, such as getFile().getAbsolutePath().
However, if modifying the target class is not feasible, then you need some way of converting a Resource into a pathname. You could use a FactoryBean for this, something like:
public class ResourcePathFactoryBean extends AbstractFactoryBean<String> {
private Resource resource;
#Required
public void setResource(Resource resource) {
this.resource = resource;
}
#Override
protected String createInstance() throws Exception {
return resource.getFile().getAbsolutePath();
}
#Override
public Class<?> getObjectType() {
return String.class;
}
}
You can then use it to inject your path:
<bean id="myBean" class="com.MyBean">
<property name="path">
<bean class="com.ResourcePathFactoryBean">
<property name="resource" value="/WEB-INF/path/to/file"/>
</bean>
</property>
</bean>
Is the file on the classpath?
In your bean, you could have a property such as Resource fileResource, and then set it in your bean like
<property name="fileResource" value="classpath:/path/to/the/file"/>
The Spring Resource interface has a method named getFile(), which will return a File handle for the resource. From there you can simply call File.getAbsolutePath().