I'm fairly new to spring so excuse me if this is a dumb question. When I try to launch a program I get the following error: java.lang.IllegalArgumentException: Could not resolve placeholder 'appclient' in string value [${appclient}]. The error is thrown when the following code is executed:
package ca.virology.lib2.common.config.spring.properties;
import ca.virology.lib2.config.spring.PropertiesConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
#Configuration
#Import({PropertiesConfig.class})
#PropertySource("${appclient}")
public class AppClientProperties {
private static final Logger log = LoggerFactory.getLogger(AppClientProperties.class);
{
//this initializer block will execute when an instance of this class is created by Spring
log.info("Loading AppClientProperties");
}
#Value("${appclient.port:}")
private int appClientPort;
#Value("${appclient.host:}")
private String appClientHost;
public int getAppClientPort() {
return appClientPort;
}
public String getAppClientHost() {
return appClientHost;
}
}
A property file called appclient.properties exists in the resources folder with the information for host and port. I'm not sure where the "${appclient}" is defined, if it is at all. Maybe it is not even defined and that is causing the problem. Do I need to change the "${appclient}" to something like "{classpath:/appclient.properties}" or am I missing something else?
You are not reading the properties file correctly. The propertySource should pass the parameter as: file:appclient.properties or classpath:appclient.properties. Change the annotation to:
#PropertySource(value={"classpath:appclient.properties"})
However I don't know what your PropertiesConfig file contains, as you're importing that also. Ideally the #PropertySource annotation should have been kept there.
If you are using Spring 3.1 and above, you can use something like...
#Configuration
#PropertySource("classpath:foo.properties")
public class PropertiesWithJavaConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
You can also go by the xml configuration like...
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:property-placeholder location="classpath:foo.properties" />
</beans>
In earlier versions.
Hopefully it will be still helpful, the application.properties (or application.yml) file must be in both the paths:
src/main/resource/config
src/test/resource/config
containing the same property you are referring
If your config file is in a different path than classpath, you can add the configuration file path as a system property:
java -Dapp.config.path=path_to_config_file -jar your.jar
I know It is an old message , but i want to add my case.
If you use more than one profile(dev,test,prod...), check your execute profile.
In my case , I forgot to add the new property in my application.properties under test folder.Adding it resolved the issue
For properties that need to be managed outside of the WAR:
<context:property-placeholder location="file:///C:/application.yml"/>
For example if inside application.yml are name and id
Then you can create bean in runtime inside xml spring
<bean id="id1" class="my.class.Item">
<property name="name" value="${name}"/>
<property name="id" value="${id}"/>
</bean>
in my case, the war file generated didn't pick up the properties file so had to clean install again in IntelliJ editor.
If the issue occurs in IntelliJ, all you have to do is to install the plugin https://plugins.jetbrains.com/plugin/7861-envfile and follow the steps given.
My solution was to add a space between the $ and the {.
For example:
#Value("${appclient.port:}")
becomes
#Value("$ {appclient.port:}")
Related
i'm looking into migrating my Spring XML config into Java. I'm having some trouble with my PlaceHolderConfigurer.
In XML i have "locations set up as
<property name="locations">
<list>
...
<value>file:////${project.home}/conf/jdbc.properties</value>
</list>
</property>
, where "project.home" is a parameter i've set with "-Dproject.home=...."
Now, i'm not sure how to do this with Java, since i can't just use
new FileSystemResource("file:////${project.home}/conf/jdbc.properties"),
So, if i want to use PropertySourcesPlaceholderConfigurer.setLocations with a system.property, how do i do that? Pointers appreciated.
A combination of context:property-placeholder and #Value annotation can be used for injecting a set of properties into Spring Beans easily.
Here is the 3-step procedure to accomplish this:
Step 1: Define all the required properties inside a 'key=value' type file
application.properties
Step 2: Specify the location of application.properties file in the bean config, using property-placeholder
Step 3: Use #Value annotation in Java program to fetch properties.
Here are the code snippets for a working example:
Step 1: Define properties in 'key=value' format
# File name: application.properties
db.schema=my_schema
db.host=abc.xyz.com:3306
db.table=my_table
Step 2: Mention the location of properties file using property-placeholder
<beans xmlns="http://www.springframework.org/schema/beans" ...>
<context:property-placeholder location="classpath:application.properties"/>
<!-- other content -->
</beans>
Step 3: Fetch properties using #Value annotation
package com.example.demo;
import org.springframework.beans.factory.annotation.Value;
public class MyProgram {
#Value("${db.host}")
private String dbHost;
#Value("${db.schema}")
private String dbSchema;
#Value("${db.table}")
private String dbTable;
#Override
public void showConfig() {
System.out.println("DB Host = " + dbSchema);
System.out.println("DB Schema = " + dbSchema);
System.out.println("DB Table = " + dbSchema);
}
}
Output of call to showConfig()
DB Host = abc.xyz.com:3306
DB Schema = my_schema
DB Table = my_table
More information:
https://memorynotfound.com/load-properties-spring-property-placeholder/
https://memorynotfound.com/loading-property-system-property-spring-value/
https://mkyong.com/spring/spring-propertysources-example/
System.getProperty will give you the property set using -D java option
Since you are attempting to call PropertySourcesPlaceholderConfigurer.setLocations() with custom locations you probably should use #PropertySource annotation.
#Configuration
#PropertySource(value="file://#{systemProperties['project.home']}/conf/jdbc.properties")
public class MyConfig {
}
You can also configure the PropertySourcesPlaceholderConfigurer bean if you need more fine grained control e.g. if your location uses patterns. See this answer for more details.
I use #ImportResource({"classpath:property-sources.xml"}) annotation to externalize path to some configuration files.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<util:properties id="externalProperties"
ignore-resource-not-found="true"
location="file:${dir.data}/foo.properties"
/>
<context:property-placeholder ignore-unresolvable="true" properties-ref="externalProperties"/>
</beans>
So, in application I can inject any property from foo.properties using #Value. Pretty common, yes.
However, when I'm trying to use #ConfigurationProperties to do do the same thing, Spring Boot completely ignores any property winthin that file. At first I've thought that something wrong with configuration, but if I put exactly the same property into application.yml it works. Compare:
foo.properties (ignored by #ConfigurationProperties resolver)
descriptions.foo.bar.baz = test
application.yml (successfully processed by #ConfigurationProperties resolver)
descriptions:
foo:
bar:
baz: test
There should be no difference. Also, there is nothing special in the bean itself:
#ConfigurationProperties(prefix = "descriptions", ignoreUnknownFields = false)
public class Descriptions {
private Map<String, String> foo = new HashMap<>();
public Map<String, String> getFoo() {
return foo;
}
public void setFoo(Map<String, String> foo) {
this.foo = foo;
}
}
Why #Value does always work and #ConfigurationProperties only works for application.yml ?
In your XML file you are loading a properties file into a Properties object, which then is passed to an additionally configured PropertySourcePlaceholderConfigurer. That is what all that namespace magic does.
The PropertySourcePlaceholderConfigurer will use the properties to resolve value expressions in #Value or xml. It will NOT add the loaded properties to the Environment. The Environment is what is being used by the ConfigurationPropertiesBindingPostProcessor in Spring Boot. This also runs very early in the process.
Instead what you should do is either put an #PropertySource on your #SpringBootApplication annotation class:
#PropertySource(value="file:${dir.data}/foo.properties", ignoreResourceNotFound=true)
Or specify which additional configuration files to load using the spring.config.additional-location property.
--spring.config.additional-location=file:${dir.data}/foo.properties
With the latter you don't need to change anything, just specify which files to load at startup.
public class someclassname{
#Value("${file.name}")
private String fileName;
FileInputStream fip = new FileInputStream(fileName);
Properties properties = new Properties();
properties.load(fip);
//read your file contents
}
The value of the file.name can be set in a properties file:
file.name=yourpath
PropertySource annotation can be used to achieve the same.
#Component
#PropertySource("classpath:foo.properties")
public class TestProperties {
#Value("${descriptions.foo.bar.baz}")
private String descriptions;
//getters and setters
}
ConfigurationProperties equivalent
import org.springframework.boot.context.properties.ConfigurationProperties;
#Configuration
#PropertySource("classpath:foo.properties")
#ConfigurationProperties
public class TestProperties {
private String descriptions;
//getters and setters
}
File foo.properties gives a Properties object which holds string=>string mapping "descriptions.foo.bar.baz" => "test". The string "descriptions.foo.bar.baz" in general can not be converted to structure like nested Map<String,Object>s.
On the other side, application.yml gives exactly the same structure as the application expects, nested Map<String,Object>s. So it works because of this, and because of type-erasure: at runtime jvm only knows that void setFoo(Map foo) expects a Map. So the application is going to throw a ClassCastException as soon as foo.get("bar") is used as String, because actual value would be a Map.
I am very new to Spring Framework. I am using NetBeans for IDE. I followed couple of tutorials to learn it by myself. However, I am stuck in the middle and cannot proceed further. Let me breakdown my project here:
My project folder structure looks like this:
There are two classes; the major one MainApp.java contains following code:
package com.myprojects.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context;
context = new ClassPathXmlApplicationContext("classpath*:beans.xml");
FirstPage obj;
obj = (FirstPage) context.getBean("firstPage");
obj.getMessage();
}
}
Second class file FirstPage.java looks like this:
package com.myprojects.spring;
public class FirstPage {
private String message;
public void setMessage(String message){
this.message = message;
}
public void getMessage(){
System.out.println("Your Message : " + message);
}
}
The beans.xml file looks like below:
<?xml version="1.0" encoding="UTF-8"?>
<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-4.0.0.RELEASE.xsd
">
<bean id = "firstPage" class = "com.myprojects.spring.FirstPage">
<property name = "message" value = "Hello World!"/>
</bean>
</beans>
Now, the error I am getting is like below:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'firstPage' is defined
I know I have been doing some silly mistake here.
Thank you in Advance !
Almost similar problem have been discussed before. I think your program is unable to locate beans.xml.
Try doing this:
context = new ClassPathXmlApplicationContext("META-INF/beans.xml");
EDIT:
This new error XmlBeanDefinitionStoreException means that your schema is not valid. Try changing your schema as described in one of these answers:
https://stackoverflow.com/a/21525719/2815219
https://stackoverflow.com/a/25782515/2815219
Spring configuration XML schema: with or without version?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id = "firstPage" class = "com.myprojects.spring.FirstPage">
<property name = "message" value = "Hello World!"/>
</bean>
</beans>
According to the directory structure you posted it is very likely that src/main/resources is on your classpath. If you like to reference your spring context file beans.xml you have to specify it relative to the folders on your classpath. Hence you should try:
context = new ClassPathXmlApplicationContext("classpath:/META-INF/beans.xml");
Besides: the notation classpath*:beans.xml means you want to read in all context files having a name of beans.xml.
Put beans.xml to outside Meta-inf ,
or use new ClassPathXmlApplicationContext("META-INF/beans.xml");
And http://www.springframework.org/schema/beans/spring-beans-4.0.0.RELEASE.xsd
should change to
http://www.springframework.org/schema/beans/spring-beans-4.0.0.xsd , as spring's xsd filenames don't contain "RELEASE".
The xsd files are in org.springframework.beans.factory.xml package in spring-beans.jar, see if the xsd file is in that package.
Doing following two things solved my issue.
1) There was an incorrect beans.xml path. I changed that to context = new ClassPathXmlApplicationContext("META-INF/beans.xml");.
2) Also, there was an invalid xsi:schemaLocation attribute value. I changed that attribute's value to http://www.springframework.org/schema/beans/spring-beans-3.0.xsd.
Thank you all for your help.
So I have been using the 3.2.0.RELEASE Spring XML configurations for most of my beans but now I am faced with a unique situation where the Getters and Setters can't be used (bad legacy code - can't get around it).
As such, I want to use Spring #Configuration class and the XML to workaround this problem.
However, I am getting "Class Not Found" exception when it tries to read my #Configuration Class.
Caused by: java.lang.ClassNotFoundException: v1.inventory.item.myJavaConfig
My XML file which is failing looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="v1.inventory.item.myJavaConfig"/>
</beans>
My #Configuration class looks like this:
package v1.inventory.item;
#Configuration
#ImportResource("classpath:v1/inventory/item/baseItemConfigs.xml")
public class myJavaConfig {
#Autowired
#Qualifier("parentItem")
Item baseItem;
#Bean
public Item realItem(){
Item modifiedBean = baseItem;
modifiedBean.setManufacturer("Fake Setter for Manufacturer");
modifiedBean.setDesigner("Fake Setter for Designer");
return modifiedBean;
}
}
I need this to be read by the ApplicationContext so I need to make sure these beans can be found. Is this a bug with Spring 3.2.0.RELEASE? Or my code?
For the record, I am pulling in the #Configuration last (parentItem is scanned first in XML).
I figured out the issue here.
It seems that maven/spring (not sure which) wasn't looking in my "resource" directory for the file. Only my "java" directory. When I moved my file into the "java" directory, Spring found the file just fine.
The tests which were passing in JUnit now pass using the Maven compiler (which was throwing the above error during the test phase)
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.