I have 2 jars with a dependency:
clientKit.jar - contains a FeignClient.java and spring #Configuration file defining the #Bean for creating the FeignClient.java:
#Configuration
public class ConnectorConfig{
#Value("${FeignClient.Host}")
private String FeignClientHost;
#Bean
public FeignClientCls feignClientCls()
{
FeignClientCls connector = Feign.builder()
.target(FeignClientCls.class, feignClientnHost);
return connector;
}
}
services.jar - contains logic. #Inject feignClient.java and uses #PropertySource to specify the .properties file containing FeignClientHost
Configuration File:
#Configuration
#PropertySource("file:/C:/projects-path/app.properties")
public class AppConfig
{
#Inject
ApplicationContext applicationContext;
// omitted code
}
Injecting the FeignClientCls (in services.jar)
public class CallFeignClient{
#Inject
protected FeignClientCls connector;
public void execute(){
connector.someMethod();
}
}
and an external app.property file defining the host and port:
FeignClient.Host=https://localhost:8444
I'm getting below exception when spring starts:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'FeignClient.Host' in string value "${FeignClient.Host}"
I checked in debug and i see that Spring doesn't have the app.properties in the PropertySourcesPropertyResolver.propertySources list when trying to obtain the value.
My question is - Can this be done and is this the correct way?
if so, why do i get the exception...
I have a similar code implemented in a different project and it works.
Thanks!
Related
I'm writing a junit test where I need to Autowire a specific implementation of an interface. I'm using the #Mock annotation to Autowire the implementation.
I'm using profiles and a Configuration file to determine which implementation to Autowire.
When running the test class EmailTest , the following error message appears on the console:
Caused by: java.lang.IllegalStateException: Unable to register mock bean .... expected a single matching bean to replace but found [customerEmailSender, emailSenderImpl_1, emailSenderImpl_2]
The reason is that the Spring doesn't find or use the config class : BeanConfiguration.
I know this because I put a breakpoint in the class BeanConfiguration, and the application doesn't break.
What could be the raeson that Spring doesn't find or use the configuration class BeanConfiguration.
#RunWith(SpringRunner.class)
#ActiveProfiles(profiles = {"test-unit"})
#Import(BeanConfiguration.class)
public class EmailTest {
#MockBean
private CustomerEmailSender customerEmailSender;
}
#Configuration
public class BeanConfiguration {
#Profile({"test-unit"})
#Bean(name = "customerEmailSender")
public CustomerEmailSender emailSenderImpl_1(){
return new EmailSenderImpl_1();
}
#Profile({"prd"})
#Bean(name = "customerEmailSender")
public CustomerEmailSender emailSenderImpl_2(){
return new EmailSenderImpl_2();
}
}
Check if BeanConfiguration package is till in spring's component scan boundary
The problem is that #MockBean does not know which bean to replace (and you have multiple beans of the same interface).
Try
#MockBean(name = "customerEmailSender")
private CustomerEmailSender customerEmailSender;
I'm using Spring (without spring-boot). I want to build standalone application that can be run with default configuration (logback.xml and application.properties in resource folder) or with -Dconfig.folder=/path/to/custom/external/directory
(logback.xml and application.properties in /path/to/custom/external/directory). When application will be run with -Dconfig.folder param AppConfig should load both logback and properties from external directory.
Is there anyway to make external folder act like a resource folder?
If not, what is a common solution for this?
My current implementation (using default resource folder only):
App.java
public class App {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
SampleAction p = context.getBean(SampleAction.class);
p.performTask();
}
}
AppConfig.java
#ComponentScan
#PropertySource("classpath:application.properties")
class AppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
SampleAction.java
#Component
public class SampleAction {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Value("${sample.prop}")
private String sampleProp;
public void performTask(){
logger.debug(sampleProp);
}
}
logback.xml and application.properties are not relevant to the problem
Unlike the other answer suggests, if you use file prefix in #PropertySource, you're screwed because it won't be able to load the default application.properties from the jar. What you should do is the following:
#PropertySource("${config.folder:'classpath:'}/application.properties")
public class AppConfig
For logback.xml:
#Value("${config.folder}:")
private String configFolder;
InputStream = Optional.of(new ClassPathResource(configFolder + "/logback.xml"))
.filter(r -> r.exists())
.orElse(new ClassPathResource("classpath:/logback.xml"))
.getInputStream();
In both cases, I gave preference to the command line argument over the default packaged files. Of course, I didn't compile the above, so there may be typos or minor errors, but you get the idea.
Edit:
Since OP claims to not understand where to run the above code -
public class AppConfig {
#PostConstruct
void init() {
// init logback here
}
}
For log4j.xml
-Dlog4j.configuration=C:\neon\log4j.xml as VM argument
In main() method:
String filename = System.getProperty("log4j.configuration");
DOMConfigurator.configure(filename);
For external properties file:
-Dext.prop.dir=C:\neon as VM argument
Change in your AppConfig class will be like
#PropertySource("file:///${ext.prop.dir}/application.properties")
public class AppConfig{
}
Run App class with VM arguments as below and both file will be use from external location
-Dlog4j.configuration=C:\neon\log4j.xml -Dext.prop.dir=C:\neon
I've got a spring boot application (1.3.1.RELEASE) and recently experienced a strange behavior when trying to inject a #ConfigurationProperties bean into my configuration. I'm not an expert in Spring, so my question is how can this behavior be explained?
So the setup is like that:
MyApplication.java:
package me.developer;
#SpringBootApplication
public class MyApplication {
public static void main(final String... args) {
SpringApplication.run(<..>, args);
}
}
SecurityProperties.java:
package me.developer.document.security;
#Setter
#Getter
#Component
#ConfigurationProperties(prefix = "security")
public class SecurityProperties {
private List<String> apiKeys = new ArrayList<>();
}
SecurityConfiguration.java
package me.developer.document;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private SecurityProperties securityProperties;
#Override
protected void configure(final HttpSecurity http) throws Exception {
<..>
}
}
When the application is started I get an error that Spring can't inject SecurityConfiguration.securityProperties because it knows nothing about me.developer.document.security.SecurityProperties bean.
But if I rename SecurityProperties to SicurityProperties (second letter "e" --> "i") - it works! Don't ask me how did I get to that, but I'm just curious how this behavior can be explained? I.e. from my perspective it should either work or not, but not depending of the bean name, etc...
Update:
I also works if I don't touch the class name but explicitly specify the bean name like that
package me.developer.document.security;
#Setter
#Getter
#Component("securityProperties.my")
#ConfigurationProperties(prefix = "security")
public class SecurityProperties {
private List<String> apiKeys = new ArrayList<>();
}
And I don't provide any additional qualifiers during autowiring... Why is it happening?
Assuming that you have a dependency on spring-boot-starter-security, Spring Boot will be auto-configuring its own SecurityProperties bean that's also annotated with #ConfigurationProperties. Unfortunately this bean will have the same name as your bean as the class names (ignoring the package) are the same. This name clash means that Spring Boot's been will be overriding your security properties bean. You should see a warning message to that effect logged during startup.
As you have observed, changing the name of your bean so that there's no longer a name clash will solve the problem.
I am trying to get Cucumber working with Spring. In our code, we are already using java based Spring configuration. I am having trouble getting it to work in the following scenario. Can someone please help?
Today , in our integration test classes we use #ContextConfiguration for each class and provide the config class that is declared with in that integration test class for loading the beans. Config class is annotated with #Configuration. Same bean could be instantiated differently in 2 different classes Config classes used in 2 different integration test classes.
So when I use Cucumber, since the Contextconfiguration differs on different classes, it looks for 'Cucumber.xml' . In the xml file, I am using component-scan to scan the cucumber step definition classes by giving the package name that these classes use (both classes have same package name) . Since all beans gets loaded in same context, Cucumber is failing to load the beans when it finds the same bean defined in these different config classes .
How do I get over this problem of creating same bean but in different ways and use them in different classes?
Please note that I am not looking for a solution that creates lot of churn from our existing coding practices, so having per-test-xml file is not an option for me.
Here is how our code looks:
Class NameAndAddressProviderIntegrationTestSteps :-
#ContextConfiguration(locations="classpath:cucumber.xml")
public class NameAndAddressProviderIntegrationTestSteps {
#Configuration
#Import({
xyz.class,
abc.class,
NameAndAddressProvider.class
})
#ImportResource({
"file:configuration/spring-configuration/abc.xml",
"file:configuration/spring-configuration/xyz.xml"
})
public static class Config {
#Bean
AccountHolderDataMap dataMap() {
AccountHolderDataMap data = new AccountHolderDataMap();
data.put(ID,
new AccountHolderData(customerID));
data.get(customerID).setCustomerplaceID(testCustomerplaceID);
return data;
}
}
#Inject
private NameAndAddressProvider provider;
#When("^I call nameandAddress provider with a 'customerId'$")
public void i_call_nameandAddress_provider_with_a_customerId() throws DependencyException {
System.out.println("Entering when method");
names = provider.getNames(customerID);
System.out.println(provider.toString());
}
......
}
Class AddressProviderIntegrationTestSteps:-
#ContextConfiguration(locations="classpath:cucumber.xml")
public class AddressProviderIntegrationTestSteps {
#Configuration
#Import({
abc.class,
xyz.class,
AddressesProvider.class
})
#ImportResource({
"file:configuration/spring-configuration/test-environment.xml",
"file:configuration/spring-configuration/test-logging-config.xml"
})
public static class Config {
#Bean
#DependsOn("Environment")
AccountHolderDataMap data() {
AccountHolderDataMap data = new AccountHolderDataMap();
data.put(testCustomerID,
new AccountHolderData(testCustomerID, testCustomerplaceID,businessType));
return data;
}
}
private static final String testCustomerID = "1234";
private static final String testMarketplaceID = "abc";
#Inject
private AddressesProvider provider;
#When("^I call AddressesProvider provider with a 'CustomerID'$")
public void i_call_AddressesProvider_provider_with_a_CustomerID() throws Throwable {
List<Address> addresses = provider.getAddresses(testCustomerID);
Log.info(addresses.get(0).toString());
assertTrue(addresses.size()==1);
}
}
And here is the nested exception I am getting:-
"nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [....AccountHolderDataMap] is defined: expected single matching bean but found 2: dataMap,data"
Appreciate your help!
I've managed multiple sources for bean-definitions. You can use this at a starting point (or others in the internet as your question is quite old)
I am using spring4, see my other cucumer post for the pom
At the stepdefs use a config.class
#ContextConfiguration(classes = { CucumberConfiguration.class })
public class StepdefsTest123 {
#Autowired bean; // from cucumberBeanContext.xml
#When("^A$")
public void a() throws Throwable {
System.out.println(bean.getFoo());
}
}
in the config-class add aditional beandefinitions
#Configuration
#ComponentScan(basePackages = "package.here.cucumber")
#ImportResource("classpath:cucumberBeanContext.xml")
public class CucumberConfiguration {
// nothing to do here
}
I want to be able to use HBase with spring. The documentation I can see has something that says:
<hdp:configuration>
fs.default.name=${hd.fs}
mapred.job.tracker=${mapred.job.tracker}
hbase.zookeeper.quorum=${hbase.zookeeper.quorum}
</hdp:configuration>
How can I programatically use this in my Application.java? There appears to be no sample or documentation on how to use HBaseTemplate to connect to hbase. Looking at the following repository as recommended in another post has no hbase-related examples:
https://github.com/spring-projects/spring-hadoop-samples
Currently I haven't found annotation based configuration for hdp:configuration, but instead for my Spring-Hadoop Project I have created a xml for haddop configuration as hadoop-context.xml and imported it in Config.java like below
#Configuration
#ImportResource ( "hadoop-context.xml")
public class Config
{
#Autowired
private org.apache.hadoop.conf.Configuration hadoopConfiguration;
}
Most probably it will help in your case to solve the issue.
I know the question is old but I just ran into this and was able to resolve. Implement SpringHadoopConfigurerAdapter like:
#Configuration
#EnableHadoop
public class HadoopConfiguration extends SpringHadoopConfigurerAdapter {
#Autowired
private ResourceLoader resourceLoader;
#Override
public void configure(HadoopConfigConfigurer config) throws Exception {
org.apache.hadoop.conf.Configuration conf = getHadoopConfig("classpath:/mapred-site.xml");
config
.withResources()
.resource("classpath:/yarn-site.xml")
.resource("classpath:/hbase-site.xml")
.resource("classpath:/mapred-site.xml")
.and()
.withProperties()
.property("mapreduce.app-submission.cross-platform", "true")
.property("mapreduce.framework.name", "yarn")
.property("mapreduce.application.framework.path", "")
.property("mapreduce.map.log.level", "INFO")
.property("mapreduce.reduce.log.level", "INFO")
.property("hbase.client.scanner.caching", "1")
.property("hbase.client.scanner.timeout.period", "300000")
.property("hbase.rpc.timeout", "300000");
}
private org.apache.hadoop.conf.Configuration getHadoopConfig(String mrSiteFile) throws IOException {
Resource mapRedSite = resourceLoader.getResource(mrSiteFile);
org.apache.hadoop.conf.Configuration conf = new org.apache.hadoop.conf.Configuration();
conf.addResource(mapRedSite.getInputStream());
return conf;
}
}
Then you'll be able to inject your HadoopConfiguration:
#Autowired
org.apache.hadoop.conf.Configuration hadoopConfiguration;