#PropertySource java.io.FileNotFoundException - java

I am testing Spring's #Conditional in which I load the bean depending on the value present in the .properties file. So I created a .properties file in src/main/resources/application-config.properties and I have the configuration class as:
#Configuration
#PropertySource(value = {"classpath:application-config.properties"}, ignoreResourceNotFound = false)
#ComponentScan(basePackages = {"com.app.test"})
public class ApplicationContextConfig {...}
I have 2 Condition implementations as below:
public class ConditionalBeanOne implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String name= conditionContext.getEnvironment().getProperty("condition.name");
return name.equalsIgnoreCase("condition_one");
}
}
public class ConditionalBeanTwo implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String name= conditionContext.getEnvironment().getProperty("condition.name");
return name.equalsIgnoreCase("condition_two");
}
}
I have respective POJO classes as:
#Component
#Conditional(value = ConditionalBeanOne.class)
public class BeanOne implements ServiceBean {}
#Component
#Conditional(value = ConditionalBeanTwo.class)
public class BeanTwo implements ServiceBean {}
When I run the application, I get following exception:
Caused by: java.io.FileNotFoundException: class path resource [application-config.properties] cannot be opened because it does not exist
I am running this through main method as following:
public class ConditionalMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationContextConfig.class);
.....
}
}

I couldn't reproduce your problem so I created a complete working example based on your use case, which is also available on my GitHub. I noticed that your conditions are really the same, only the values are different, so you don't really need to duplicate the code there. Other than that, it's pretty much what you did.
I'd say that you're reinventing the wheel here. Spring Boot already has a ConditionalOnProperty which does this.
Application.java:
public class Application {
public static void main(String[] args) {
try (AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(ApplicationConfig.class)) {
ApplicationConfig.GreeterService greeterService =
applicationContext.getBean(ApplicationConfig.GreeterService.class);
String actual = greeterService.greeting();
System.out.printf("Greeting: %s.\n", actual);
}
}
}
ApplicationConfig.java:
#Configuration
// The / doesn't matter, but I prefer being explicit
#PropertySource("classpath:/application.properties")
#ComponentScan
public class ApplicationConfig {
#FunctionalInterface
public static interface GreeterService {
String greeting();
}
#Bean
#ConditionalOnProperty("hello")
public GreeterService helloService() {
return () -> "hello";
}
#Bean
#ConditionalOnProperty("hi")
public GreeterService hiService() {
return () -> "hi";
}
}
ConditionalOnProperty.java:
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Conditional(OnPropertyCondition.class)
public #interface ConditionalOnProperty {
String value() default "";
}
OnPropertyCondition.java:
public class OnPropertyCondition implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Map<String, Object> attributes = annotatedTypeMetadata
.getAnnotationAttributes(ConditionalOnProperty.class.getName());
String value = (String) attributes.get("value");
String name = conditionContext.getEnvironment().getProperty("greeting");
return !isEmpty(name) && name.equalsIgnoreCase(value);
}
}
application.properties:
greeting=hello
Run normally:
Output:
Greeting: hello.
Run with -Dgreeting=hi:
Output:
Greeting: hi.

Related

dynamically set #JmsListener destination from configuration properties

I want to be able to set the #JMSlistener destination from an application.properties
my code looks like this
#Service
public class ListenerService {
private Logger log = Logger.getLogger(ListenerService.class);
#Autowired
QueueProperties queueProperties;
public ListenerService(QueueProperties queueProperties) {
this.queueProperties = queueProperties;
}
#JmsListener(destination = queueProperties.getQueueName() )
public void listenQueue(String requestJSON) throws JMSException {
log.info("Received " + requestJSON);
}
}
but when building I get
Error:(25, 60) java: element value must be a constant expression
You can't reference a field within the current bean, but you can reference another bean in the application context using a SpEL expression...
#SpringBootApplication
public class So49368515Application {
public static void main(String[] args) {
SpringApplication.run(So49368515Application.class, args);
}
#Bean
public ApplicationRunner runner(JmsTemplate template, Foo foo) {
return args -> template.convertAndSend(foo.getDestination(), "test");
}
#JmsListener(destination = "#{#foo.destination}")
public void listen(Message in) {
System.out.println(in);
}
#Bean
public Foo foo() {
return new Foo();
}
public class Foo {
public String getDestination() {
return "foo";
}
}
}
You can also use property placeholders ${...}.
Using property placeholder is much easier.
#JmsListener(destination = "${mq.queue}")
public void onMessage(Message data) {
}

get #Qualifier name from database based on condition at runtime

I have set qualifier name from properties file as isomessage.qualifier=isoMessageMember1:
public class BankBancsConnectImpl implements BankBancsConnect{
#Autowired
#Resource(name="${isomessage.qualifier}")
private Iso8583Message iso8583Message;
public BancsConnectTransferComp getFundTransfer(IpsDcBatchDetail ipsDcBatchDetail) {
bancsxfr = iso8583Message.getFundTransfer(bancsxfr);
}
}
The value of ${isomessage.qualifier} is static as it is defined in the properties file. However i want it to be dynamic and get it's value from database based on certain condition. For instance i have multiple implementation of Iso8583Message (member wise) and has to call respective class of member id that is currently logged in. Please guide me to achieve this in the best and java spring way.
And my implementation class will look like this:
#Service("isoMessageMember1")
public class Iso8583MessageEBLImpl implements Iso8583Message{
public BancsConnectTransferComp getFundTransfer(BancsConnectTransferComp bancsxfr) throws Exception {
...
}
You can use Condition instead Qualifier if you are using Spring4+.
First, you need a ConfigDAO which read the qualifier name which you
need from database.
public class ConfigDAO {
public static String readFromDataSource() {
return " ";
}
}
Suppose there are two implemetions of Iso8583Message, you can
create two Condition objects.
IsoMessageMember1_Condition
public class IsoMessageMember1_Condition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String qualifier = ConfigDAO.readFromDataSource();
if (qualifier.equals("IsoMessageMember1_Condition")) {
return true;
} else {
return false;
}
}
}
IsoMessageMember2_Condition
public class IsoMessageMember2_Condition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String qualifier = ConfigDAO.readFromDataSource();
if (qualifier.equals("IsoMessageMember2_Condition")) {
return true;
} else {
return false;
}
}
}
Return different implemetion according to condition in config class.
#Configuration
public class MessageConfiguration {
#Bean(name = "iso8583Message")
#Conditional(IsoMessageMember1_Condition.class)
public Iso8583Message isoMessageMember1() {
return new Iso8583MessageEBLImpl();
}
#Bean(name = "iso8583Message")
#Conditional(IsoMessageMember2_Condition.class)
public Iso8583Message isoMessageMember2() {
return new OtherMessageEBLImpl();
}
}
Remove the #Qulifier and #Autowire annotations which you do not need anymore, you can retrieve the message from context every time you use it.
public class BankBancsConnectImpl implements BankBancsConnect{
private Iso8583Message iso8583Message;
public BancsConnectTransferComp getFundTransfer(IpsDcBatchDetail ipsDcBatchDetail) {
iso8583Message = (Iso8583Message)context.getBean("iso8583Message");
bancsxfr = iso8583Message.getFundTransfer(bancsxfr);
}
}
In spring it is possible to autowire the application context, and retrieve any bean based on its name.
For example, your interface signature similar to the below syntax
public interface Iso8583Message {
public String getFundDetails(String uniqueId);
}
and 2 different implementations follow below format
#Service("iso8583-message1")
public class Iso8583MessageImpl1 implements Iso8583Message {
#Override
public String getFundDetails(String uniqueId) {
return "Iso8583MessageImpl1 details ";
}
}
and
#Service("iso8583-message2")
public class Iso8583MessageImpl2 implements Iso8583Message {
#Override
public String getFundDetails(String uniqueId) {
return "Iso8583MessageImpl2 details ";
}
}
We can retrieve the beans as follows
public class BankBancsConnectImpl implements BankBancsConnect{
#Autowired
private ApplicationContext applicationContext;
public BancsConnectTransferComp getFundTransfer(IpsDcBatchDetail
ipsDcBatchDetail) {
//for retrieving 1st implementation
Iso8583Message iso8583Message=applicationContext.getBean("iso8583-message1", Iso8583Message.class);
//For retrieving 2nd implementation
Iso8583Message iso8583Message=applicationContext.getBean("iso8583-message2", Iso8583Message.class);
String result = iso8583Message.getFundTransfer(bancsxfr);
}
}
In this case, we can configure the bean names coming from the database instead of hard coded values("iso8583-message1","iso8583-message2").

Spring PropertySourcesPlaceholderConfigurer does not use custom PropertySource for #Value

I have been trying to get a very basic example of a custom PropertySource running in a Spring Application.
This is my PropertySource:
public class RemotePropertySource extends PropertySource{
public RemotePropertySource(String name, Object source) {
super(name, source);
}
public RemotePropertySource(String name) {
super(name);
}
public Object getProperty(String s) {
return "foo"+s;
}
}
It gets added to the ApplicationContext via an ApplicationContextInitializer:
public class RemotePropertyApplicationContextInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
public void initialize(GenericApplicationContext ctx) {
RemotePropertySource remotePropertySource = new RemotePropertySource("remote");
ctx.getEnvironment().getPropertySources().addFirst(remotePropertySource);
System.out.println("Initializer registered PropertySource");
}
}
Now I created a simple Unit-Test to see if the PropertySource is used correctly:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = RemotePropertySourceTest.ContextConfig.class, initializers = RemotePropertyApplicationContextInitializer.class)
public class RemotePropertySourceTest {
#Autowired
private UnderTest underTest;
#Autowired
Environment env;
#Test
public void testContext() {
assertEquals(env.getProperty("bar"),"foobar");
assertEquals(underTest.getFoo(),"footest");
}
#Component
protected static class UnderTest {
private String foo;
#Autowired
public void setFoo(#Value("test")String value){
foo=value;
}
public String getFoo(){
return foo;
}
}
#Configuration
#ComponentScan(basePackages = {"test.property"})
protected static class ContextConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
return configurer;
}
}
}
Accessing the value via the Environment gives me the correct result ("foobar"), but using the #Value-Annotation fails. As far as I have read in the documentation the PropertySourcesPlaceholderConfigurer in my Configuration should automatically pick up my PropertySource from the environment but apparently it does not. Is there something I am missing?
I know that accessing properties explicitly via the environment is preferrable but the existing application uses #Value-Annotations a lot.
Any help is greatly appreciated. Thanks!
To get value from property source with #Value you have to use ${} syntax:
#Autowired
public void setFoo(#Value("${test}")String value){
foo=value;
}
Take a look at official documentation.

Spring Boot : How to get program args with ConfigurationProperties

I'm trying to get some properties from the command line when I start my app. I really try a lot of solution but nothings is working. I'll only use #Value as a last solution
java -jar myjar.jar --test.property=something
Here's some classes
Main class
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(TestApplication.class);
builder.run(args);
}
}
Config class
#Configuration
#EnableConfigurationProperties(StringProperties.class)
public class StringConfiguration {
#Autowired
private StringProperties stringProperties;
#Bean(name = "customBeanName")
public List<String> properties() {
List<String> properties = new ArrayList<>();
properties.add(stringProperties.getString());
return properties;
}
}
Properties class
#Component
#ConfigurationProperties("test")
public class StringProperties {
private String property;
public String getString() {
return property;
}
}
Ops.....I have tested your code.It is setProperty that missed in StringProperties.
public void setProperty(String property) {
this.property = property;
}

Annotation based ServiceLocatorFactoryBean?

I would like to implement Factory pattern in my project..i have gone through online resources and I came to know that spring ServiceLocatorFactoryBean should be implemented instead of normal java factory pattern....
i have followed this link but it is explained in xml based....can any one tell me how to do it using annotations based Factory pattern??
Spring Java Configuration ref guide #Configuration
Interface Parser.class
public interface Parser {
void parse(String str);
}
Implementation for above interface.
JsonParser.java
public class JsonParser implements Parser {
#Override
public void parse(String str) {
System.out.println("JsonParser.parse::" + str);
}
}
XMLParser.java
public class XMLParser implements Parser{
#Override
public void parse(String str) {
System.out.println("XMLParser.parse :: " + str);
}
}
ParserFactory.java actual Factory interface.
public interface ParserFactory {
public Parser getParser(ParserType parserType);
}
ParseType.java enum to specify parsing types(avoid typos and safe)
public enum ParserType {
JSON("jsonParser"), XML("xmlParser");
private final String value;
ParserType(String input) {
this.value = input;
}
public String getValue() {
return this.value;
}
#Override
public String toString() {
return this.value;
}
}
ParseService.java , where Business logic implemeted.
#Service
public class ParserService {
#Autowired
private ParserFactory parserFactory;
public void doParse(String parseString, ParserType parseType) {
Parser parser = parserFactory.getParser(parseType);
System.out.println("ParserService.doParse.." + parser);
parser.parse(parseString);
}
}
Finally AppConfig.java Spring java configuration class, where all of my beans registered as container managed beans.
#Configuration
#ComponentScan(basePackages = {"<Your Package Name>"})
public class AppConfig {
#Bean
public FactoryBean serviceLocatorFactoryBean() {
ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
factoryBean.setServiceLocatorInterface(ParserFactory.class);
return factoryBean;
}
#Bean(name = "jsonParser")
#Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public JsonParser jsonParser() {
return new JsonParser();
}
#Bean(name = "xmlParser")
#Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public XMLParser xmlParser() {
return new XMLParser();
}
}
Now autowire ParserService bean in either controller or test classs, and invoke parese(String, ParseType) method to test.
Here is my test.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AppConfig.class)
public class ServiceLocatorFactoryExample {
#Autowired
private ParserService parserService;
#Test
public void testParserFactory() {
parserService.doParse("Srilekha", ParserType.JSON);
parserService.doParse("Srilekha", ParserType.XML);
}
}
Look this complete example: Serevice Locator factory
It helps me to understand how it works using spring boot.

Categories