spring integration sftp error: Dispatcher has no subscribers - spring-integration

I'm trying to use the outbound gateway to download files from sftp server,
my config:
#IntegrationComponentScan
#EnableIntegration
#Configuration
public class FtpConfig {
#Bean(name = "myGateway")
#ServiceActivator(inputChannel = "sftpChannel")
public MessageHandler handlerLs() {
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpSessionFactory(), "mget", "payload");
sftpOutboundGateway.setLocalDirectory(new File("/Users/xxx/Documents/"));
return sftpOutboundGateway;
}
#MessagingGateway
public interface OutboundGatewayOption {
#Gateway(requestChannel = "sftpChannel")
List<File> mget(String dir);
}
#Bean
public MessageChannel sftpChannel() {
return new DirectChannel();
}
}
and the execute bean:
#Service
public class DownloadService implements InitializingBean{
#Autowired
FtpConfig.OutboundGatewayOption gatewayOption;
#Override
public void afterPropertiesSet() throws Exception {
List<File> files = gatewayOption.mget("/sftp/server/path");
}
}
and I got this exception:org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'application.sftpChannel'.;
Qestion :how can I add the 'subscribers' ?

You can't perform messaging operator in the afterPropertiesSet(). That's too early : some beans might not be initialized yet. And that exception confirms the problem.
You have to implement SmartLifecicle instead and do the same in the start().

Related

Spring Integration - #InboundChannelAdapter polling

I am new to Spring Integration. We are creating our application using Spring Integration Annotations.
I have configured an #InboundChannelAdapter with poller fixed delay of 5 seconds. But the problem is as soon as I start my application on weblogic, the adapter starts polling and hits endpoint with practically no message.
We need to call a rest service and then trigger this adapter.
Is there a way to implement the same?
TIA!
Set the autoStartup property to false and use a control bus to start/stop it.
#SpringBootApplication
#IntegrationComponentScan
public class So59469573Application {
public static void main(String[] args) {
SpringApplication.run(So59469573Application.class, args);
}
}
#Component
class Integration {
#Autowired
private ApplicationContext context;
#InboundChannelAdapter(channel = "channel", autoStartup = "false",
poller = #Poller(fixedDelay = "5000"))
public String foo() {
return "foo";
}
#ServiceActivator(inputChannel = "channel")
public void handle(String in) {
System.out.println(in);
}
#ServiceActivator(inputChannel = "controlChannel")
#Bean
public ExpressionControlBusFactoryBean controlBus() {
return new ExpressionControlBusFactoryBean();
}
}
#MessagingGateway(defaultRequestChannel = "controlChannel")
interface Control {
void send(String control);
}
#RestController
class Rest {
#Autowired
Control control;
#PostMapping("/foo/{command}")
public void trigger(#PathVariable String command) {
if ("start".equals(command)) {
control.send("#'integration.foo.inboundChannelAdapter'.start()");
}
}
}

how to use SFTP Outbound Gateway 'mget' command to download files?

I want to use the 'mget' command to download files from sftp server. here's my java config:
#Bean
public SessionFactory<ChannelSftp.LsEntry> sftpSessionFactory() {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
....
return new CachingSessionFactory<>(factory);
}
#Bean(name = "lsGateway")
#ServiceActivator(inputChannel = "sftpChannel")
public MessageHandler handlerLs() {
// call 'mget' command to download all the files in server folder
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpSessionFactory(), "mget", "payload");
sftpOutboundGateway.setLocalDirectory(new File("/local/path"));
return sftpOutboundGateway;
}
gateway interface:
#MessagingGateway
public interface OutboundGatewayOption {
#Gateway(requestChannel = "sftpChannel")
List<File> mget(String dir);
}
execute download:
#Component
public class Step1Tasklet implements Tasklet {
#Autowired
private OutboundGatewayOption gatewayOption;
#Override
public RepeatStatus execute(StepContribution stepContribution,
ChunkContext chunkContext) throws Exception {
// download files in server folder
List<File> files = gatewayOption.mget("/ftp/server/path/");
return RepeatStatus.FINISHED;
}
}
I got this exception :
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'sftpChannel' available
I've google around but cannot found the issue ,is anyone kindly help please!
No bean named 'sftpChannel'
Means you don't have sftpChannel MessageChannel bean yet.
I think the problem comes from the #MessagingGateway bean definition.
What you need to do is just declare that sftpChannel bean:
#Bean
public MessageChannel sftpChannel() {
return new DirectChannel();
}
Right, in the newest Spring Integration this problem has been fixed and MessageChannel is resolved from its name lately on demand.

spring integration : solutions/tips on connect multiple sftp server?

My spring batch project needs to download files from multiple sftp servers.
the sftp host/port/filePath is config in application.properties file. I consider using the spring integration 'sftp out-bound gateway' to connect these servers and download files. but Im don't how to do this kind of configuration(I'm using java config, ) and make it work? i guess I need some way to define multiple session factory according to the number of sftp server info config in application.properties file.
properties file:
sftp.host=host1,host2
sftp.user=user1,user2
sftp.pwd=pwd1,pwd2
config class:
#Bean
public SessionFactory<ChannelSftp.LsEntry> sftpSessionFactory1() {
...
}
#Bean(name = "myGateway1")
#ServiceActivator(inputChannel = "sftpChannel1")
public MessageHandler handler1() {
...
}
#MessagingGateway
public interface DownloadGateway1 {
#Gateway(requestChannel = "sftpChannel1")
List<File> start(String dir);
}
#Bean(name="sftpChannel1")
public MessageChannel sftpChannel1() {
return new DirectChannel();
}
Right, the server is specified in the session factory, not the gateway. The framework does provide a delegating session factory, allowing it to be selected from one of the configured factories for each message sent to the gateway. See Delegating Session Factory.
EDIT
Here's an example:
#SpringBootApplication
public class So46721822Application {
public static void main(String[] args) {
SpringApplication.run(So46721822Application.class, args);
}
#Value("${sftp.name}")
private String[] names;
#Value("${sftp.host}")
private String[] hosts;
#Value("${sftp.user}")
private String[] users;
#Value("${sftp.pwd}")
private String[] pwds;
#Autowired
private DelegatingSessionFactory<?> sessionFactory;
#Autowired
private SftpGateway gateway;
#Bean
public ApplicationRunner runner() {
return args -> {
try {
this.sessionFactory.setThreadKey("one"); // use factory "one"
this.gateway.send(new File("/tmp/f.txt"));
}
finally {
this.sessionFactory.clearThreadKey();
}
};
}
#Bean
public DelegatingSessionFactory<LsEntry> sessionFactory() {
Map<Object, SessionFactory<LsEntry>> factories = new LinkedHashMap<>();
for (int i = 0; i < this.names.length; i++) {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory();
factory.setHost(this.hosts[i]);
factory.setUser(this.users[i]);
factory.setPassword(this.pwds[i]);
factories.put(this.names[i], factory);
}
// use the first SF as the default
return new DelegatingSessionFactory<LsEntry>(factories, factories.values().iterator().next());
}
#ServiceActivator(inputChannel = "toSftp")
#Bean
public SftpMessageHandler handler() {
SftpMessageHandler handler = new SftpMessageHandler(sessionFactory());
handler.setRemoteDirectoryExpression(new LiteralExpression("foo"));
return handler;
}
#MessagingGateway(defaultRequestChannel = "toSftp")
public interface SftpGateway {
void send(File file);
}
}
with properties...
sftp.name=one,two
sftp.host=host1,host2
sftp.user=user1,user2
sftp.pwd=pwd1,pwd2

How to do channel interceptor based on pattern using JAVA DSL in Spring Integration?

We are planning to migrate our code from Spring integration XML to DSL. In XML Version, we are using channel name pattern to do tracing.
For Eg: If channel name has *_EL_*, we intercept the channel and do some logging.
How to do this kind or more simpler in Java dsl.
The #GlobalChannelInterceptor is for you. And it is a part of Spring Integration Core.
So, you must do something like this:
#Bean
public MessageChannel bar() {
return new DirectChannel();
}
#Bean
#GlobalChannelInterceptor(patterns = "*_EL_*")
public WireTap baz() {
return new WireTap(this.bar());
}
I mean specify the ChannelInterceptor #Bean and mark it with that annotation to make pattern-based interceptions.
UPDATE
The sample test-case which demonstrate the work for #GlobalChannelInterceptor for the auto-created channel from DSL flows:
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#DirtiesContext
public class SO31573744Tests {
#Autowired
private TestGateway testGateway;
#Autowired
private PollableChannel intercepted;
#Test
public void testIt() {
this.testGateway.testIt("foo");
Message<?> receive = this.intercepted.receive(1000);
assertNotNull(receive);
assertEquals("foo", receive.getPayload());
}
#MessagingGateway
public interface TestGateway {
#Gateway(requestChannel = "testChannel")
void testIt(String payload);
}
#Configuration
#EnableIntegration
#IntegrationComponentScan
public static class ContextConfiguration {
#Bean
public IntegrationFlow testFlow() {
return IntegrationFlows.from("testChannel")
.channel("nullChannel")
.get();
}
#Bean
public PollableChannel intercepted() {
return new QueueChannel();
}
#Bean
#GlobalChannelInterceptor(patterns = "*Ch*")
public WireTap wireTap() {
return new WireTap(intercepted());
}
}
}

How to wire tap a message channel in java dsl?

I am trying Java dsl in spring integration. I am trying to wiretap a channel. But getting error,
#ContextConfiguration
#EnableIntegration
#IntegrationComponentScan
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(DemoApplication.class);
CustomGtwy service = ctx.getBean(CustomGtwy.class);
service.pushMessage("Manoj");
}
#Bean
public MessageChannel loggerChannel(){
return MessageChannels.direct().get();
}
#Bean
public MessageChannel pushAssetIdChannel() {
return MessageChannels.direct()
.interceptor(new WireTap(loggerChannel()))
.get();
}
#Bean
public IntegrationFlow pushAssetIdFlow() {
return IntegrationFlows.from("pushAssetIdChannel")
.handle(new GenericHandler() {
#Override
public String handle(Object arg0, Map arg1) {
// TODO Auto-generated method stub
return "Success";
}})
.get();
}
#MessagingGateway
public interface CustomGtwy{
#Gateway(requestChannel="pushAssetIdChannel")
String pushMessage(String s);
}
#Bean
public IntegrationFlow logger(){
return IntegrationFlows.from("loggerChannel").handle(new GenericHandler() {
#Override
public String handle(Object arg0, Map arg1) {
// TODO Auto-generated method stub
return "Success";
}}).channel("nullChannel").get();
}
}
In the above code, if i try to put message in the pushAssetIdChannel, i am getting Dispatcher has no subscribers for channel 'unknown.channel.name'
If the interceptor is not there, it is working.
Not sure what's going on with your case, but that works for me with the latest 1.0.2 version:
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#DirtiesContext
public class SO31348246Tests {
#Autowired
private MessageChannel pushAssetIdChannel;
#Test
public void testIt() {
this.pushAssetIdChannel.send(new GenericMessage<>("foo"));
}
#Configuration
#EnableIntegration
public static class ContextConfiguration {
#Bean
public MessageChannel loggerChannel() {
return MessageChannels.direct().get();
}
#Bean
public MessageChannel pushAssetIdChannel() {
return MessageChannels.direct()
.interceptor(new WireTap(loggerChannel()))
.get();
}
#Bean
public IntegrationFlow pushAssetIdFlow() {
return IntegrationFlows.from("pushAssetIdChannel")
.handle(System.out::println)
.get();
}
#Bean
public IntegrationFlow logger() {
return IntegrationFlows.from("loggerChannel")
.handle((p, h) -> {
System.out.println(p);
return p;
})
.channel("nullChannel")
.get();
}
}
}
I see both SOUTs in logs.
UPDATE
Your class works for me after fixing #ContextConfiguration to the normal #Configuration annotation :-).
Without the last one the framework considers your DemoApplication as lite configuration, just because you have there #Bean methods, but it does not do it like the full one and doesn't proxy it to allow to use bean method reference like you do with loggerChannel() in the WireTap constructor.
So, with lite we jsut invoke that method and get a fresh MessageChannel object, but it isn't a bean in the application context. That's why you end up with the Dispatcher has no subscribers.

Resources