2

For spring boot + integration application, I'm attempting to load configuration from database, allow it to be accessible to Spring's Environment & inject-able via @Value annotation and be override-able by externalized configurations as described in the spring boot reference documentation under the Externalized Configuration Section.

The problem I'm having is that my spring Integration XML contains ${input} property placeholders that can not be resolved, because I can't get the database backed configuration to be loaded before Spring attempts to load the XML configurations.

The entry point to the application:

@SpringBootApplication
public class TestApplication {
   public static void main(String[] args) {
      SpringApplication.run(TestApplication.class, args);
   }
}

How database configuration would be loaded:

 @Configuration
 public class DbPropertiesConfig {

    @Autowired
    private org.springframework.core.env.Environment env;

    @PostConstruct
    public void initializeDatabasePropertySourceUsage() {
        MutablePropertySources propertySources = ((ConfigurableEnvironment) env).getPropertySources();
       try {
          // The below code will be replace w/ code to load config from DB
          Map<String,Object> map = new HashMap<>();
          map.put("input-dir","target/input");
          map.put("output-dir","target/output");
          DbPropertySource dbPropertySource = new DbPropertySource("si",map);
          propertySources.addLast(dbPropertySource);
       } catch (Exception e) {
           throw new RuntimeException(e);
       }
    }
 }

How Spring Integration config is loaded:

 @Profile("IN")
 @Configuration
 @ImportResource({"si-common-context.xml","si-input-context.xml"})
 public class SiInputAppConfig {
 }

The Spring Integration XML configurationsi-input-context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:int="http://www.springframework.org/schema/integration"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:int-file="http://www.springframework.org/schema/integration/file"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
                  http://www.springframework.org/schema/beans/spring-beans.xsd
                  http://www.springframework.org/schema/integration
                  http://www.springframework.org/schema/integration/spring-integration.xsd
                  http://www.springframework.org/schema/integration/file
                  http://www.springframework.org/schema/integration/file/spring-integration-file.xsd" default-lazy-init="true">


    <int-file:inbound-channel-adapter channel="input2" directory="${input-dir}" filename-pattern="*">
        <int:poller fixed-rate="500"/>
    </int-file:inbound-channel-adapter>

       <int:service-activator input-channel="input2" ref="sampleEndpoint" method="hello" output-channel="output2"/>

    <int:channel id="output2"/>

    <int-file:outbound-channel-adapter channel="output2" directory="${output-dir}"/>

</beans:beans>

The error I get:

2015-10-28 17:22:18.283  INFO 3816 --- [           main] o.s.b.f.xml.XmlBeanDefinitionReader      : Loading XML bean definitions from class path resource [si-common-context.xml]
2015-10-28 17:22:18.383  INFO 3816 --- [           main] o.s.b.f.xml.XmlBeanDefinitionReader      : Loading XML bean definitions from class path resource [si-mail-in-context.xml]
2015-10-28 17:22:18.466  INFO 3816 --- [           main] o.s.b.f.config.PropertiesFactoryBean     : Loading properties file from URL [jar:file:/C:/Users/xxx/.m2/repository/org/springframework/integration/spring-integration-core/4.1.6.RELEASE/spring-integration-core-4.1.6.RELEASE.jar!/META-INF/spring.integration.default.properties]
2015-10-28 17:22:18.471  INFO 3816 --- [           main] o.s.i.config.IntegrationRegistrar        : No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created.
2015-10-28 17:22:18.604  INFO 3816 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'beanNameViewResolver': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; ...
2015-10-28 17:22:18.930  WARN 3816 --- [           main] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt

org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean#0.source' defined in null: Could not resolve placeholder 'si.in-input' in string value "${si.in-input}"; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'input-dir' in string value "${input-dir}" ...

Spring loads DbPropertiesConfig after XML configuration is attempted to be loaded.

How can I resolve this issue?

Thanks in advance

AP

ap1331
  • 23
  • 3

1 Answers1

1

Yes, I can confirm by my home tests that XML definitions are loaded before the annotation stuff. It's enough complicated to determine why it is, but I'm sure that @Import* resources are more important by premise than an internal @Configuration logic.

Therefore your @PostConstruct isn't good for mix with XML.

One solution is to move all Spring Integration configuration to the Annotation style and even consider to use Spring Integration Java DSL.

Another solution is to follow with Spring Boot's Externalized Configuration recommendation:

  1. Default properties (specified using SpringApplication.setDefaultProperties).

That means you have to read properties from your DB before the starting Spring Application. Yes, if you were going to use DataSource on the matter from the same application, it won't be possible. From other side let's take a look to your goal one more time! You are going to load properties for the application from DB as an external configuration, so, what is the point to do that from the application itself? Of course, it would be more safe to load and feed properties before application start.

Hope I am clear.

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • Artem, thanks for the reply. I'll evaluate the Java DSL. But for now, regarding my goal : Ideally I want to load some basic config information to be able to connect to DB, and the application will have a lot of other config setting that I like to centralize, since there will be multiple instances of this application running and I don't want to duplicate the configs in various location. If there is another way of achieving this centralization of bulk of the configuration w/ ability to override them locally, w/o going to a central database of some sort, please let me know. – ap1331 Oct 29 '15 at 16:24