Search code examples
javaspringspring-integration

How to handle optional beans when their availability criteria comes from a database?


Consider the following code which configures the polling of some SFTP servers. The configuration comes from a database.

@Bean
@RefreshScope
RotatingServerAdvice advice(DelegatingSessionFactory<LsEntry> delegatingSessionFactory) {

    List<RotationPolicy.KeyDirectory> keyDirectories = interfaceRepo.findAll().stream()
            .filter(Interface::isReceivingData)
            .map(x -> new RotationPolicy.KeyDirectory(
                    new SessionFactoryKey(x.getHostname(), x.getPort(), x.getUsername()),
                    x.getDirectory()()))
            .toList();

    return keyDirectories.isEmpty() ? null : new RotatingServerAdvice(delegatingSessionFactory, keyDirectories);

}

@Bean
ConcurrentMetadataStore store() {
    return new PropertiesPersistingMetadataStore();
}

@Bean
public IntegrationFlow flow(ObjectProvider<RotatingServerAdvice> adviceProvider,
        DelegatingSessionFactory<LsEntry> delegatingSessionFactory, ConcurrentMetadataStore store) {
    
    RotatingServerAdvice advice = adviceProvider.getIfAvailable();

    return advice == null ? null
            : IntegrationFlows
                    .from(Sftp.inboundAdapter(delegatingSessionFactory)
                            .filter(new SftpPersistentAcceptOnceFileListFilter(store, "rotate_"))
                            .localDirectory(new File("C:\\tmp\\sftp"))
                            .localFilenameExpression("#remoteDirectory + T(java.io.File).separator + #root")
                            .remoteDirectory("."), e -> e.poller(Pollers.fixedDelay(1).advice(advice)))
                    .channel(MessageChannels.queue("files")).get();
}

I'm trying to handle the case when the database is empty. I can't create a RotatingServerAdvice with an empty List<RotationPolicy.KeyDirectory> because there is an Assert.notNull in Spring's code, so I'm returning null instead of a RotatingServerAdvice instance.

In flow I tried to handle the null case with ObjectProvider when I call getIfAvailable on it it just returns me a CGLib proxy backed with what I suspect to be a NullBean (because toString() returns "null").

I have no solution to test if that bean is a NullBean or an actual instance of RotatingServerAdvice because of the proxy that hides the internal implementation.

Also there is no @Conditional annotation that would allow me to query the database.

How do I solve this?


Solution

  • This works for me in some my projects:

     @Autowired(required = false) RotatingServerAdvice rotatingServerAdvice
    

    However the IntegrationFlow is not designed for refreshing: it is a composite logical component to populated Spring Integration beans according to the provided config.

    I would suggest to implement some delegating MessageSourceMutator which would use that RotatingServerAdvice if that is not null, otherwise default method impls. This way you always can inject it into your flow and can mark it with the @RefreshScope to be able to re-initialize with newly created RotatingServerAdvice delegate. Same @Autowired(required = false) injection still applies.