I have this configuration:
@Configuration
@EnableIntegration
public class SftpConfiguration {
@Autowired
private InterfaceRepository interfaceRepo;
public record SessionFactoryKey(String host, int port, String user) {
}
@Bean
SessionFactoryLocator<LsEntry> sessionFactoryLocator() {
Map<Object, SessionFactory<LsEntry>> factories = interfaceRepo.findAll().stream()
.map(x -> new SimpleEntry<>(new SessionFactoryKey(x.getHostname(), x.getPort(), x.getUsername()),
sessionFactory(x.getHostname(), x.getPort(), x.getUsername(), x.getPassword())))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (a, b) -> a));
return new DefaultSessionFactoryLocator<>(factories);
}
@Bean
RemoteFileTemplate<LsEntry> fileTemplateResolver(DelegatingSessionFactory<LsEntry> delegatingSessionFactory) {
return new SftpRemoteFileTemplate(delegatingSessionFactory);
}
@Bean
DelegatingSessionFactory<LsEntry> delegatingSessionFactory(SessionFactoryLocator<LsEntry> sessionFactoryLocator) {
return new DelegatingSessionFactory<>(sessionFactoryLocator);
}
@Bean
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
PropertiesPersistingMetadataStore store() {
return new PropertiesPersistingMetadataStore();
}
@Bean
public IntegrationFlow flow(ObjectProvider<RotatingServerAdvice> adviceProvider,
DelegatingSessionFactory<LsEntry> delegatingSessionFactory, PropertiesPersistingMetadataStore 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();
}
private SessionFactory<LsEntry> sessionFactory(String host, int port, String user, String password) {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
factory.setHost(host);
factory.setPort(port);
factory.setUser(user);
factory.setPassword(password);
factory.setAllowUnknownKeys(true);
return factory;
}
}
Basically it provides a RemoteFileTemplate
that allows to upload files via SFTP, and an IntegrationFlow
that polls a set of SFTP servers to retrieve files. The configuration is loaded via a database.
I want to reload the beans when the configuration has changed in the database but I can't figure out how.
I think the only chance I have to make it work is to use lazy proxies because the client code has already loaded bean instances which cannot be unloaded. That's why I tried @RefreshScope
from spring cloud, but it didn't work because IntegrationFlow
forbids other scopes than singleton
.
Is there any solution other than closing the application context and run SpringApplication.run
again?