Use jdbcPollingChannelAdapter to perform multiple queries - spring-integration

I am new to spring integration, I have requirements similar to the below, data is being read from multiple tables. I am trying to implement jdbcpollingchanneladapter and jdbcMessageHandler, can anyone help with an example using java DSL?
Read the list of people
For each person get a list of departments
For each department get a list of managers
For each manager get the list of people
b. process the people information
c. write the information into a csv
I am using MessageSource to before DB queries and mapping result values to POJO by using a Mapper. How do I use the results as lookup values to perform another query.
#EnableIntegration
public class IntegrationConfig {
#Autowired
DataSource dataSource;
#Autowired
PlatformTransactionManager transactionManager;
#Bean
public MessageChannel FamilyChannel() {
return new DirectChannel();
}
#Bean
#InboundChannelAdapter(value = "FamilyChannel", poller = #Poller(fixedDelay = "1000"))
public MessageSource<Object> familyNumberMessageSource() {
JdbcPollingChannelAdapter jdbcPollingChannelAdapter = new JdbcPollingChannelAdapter(dataSource, NCFBulkOrderQuery.SELECT_NCF_BULK_ORDER_DATE);
return jdbcPollingChannelAdapter;
}
#Bean
public MessageHandler familyNumberHandler(){
JdbcMessageHandler jdbcMessageHandler = new JdbcMessageHandler(dataSource, NCFBulkOrderQuery.SELECT_NCF_BULK_ORDER_DATE);
return jdbcMessageHandler;
}
#Bean
public IntegrationFlow jdbFlow() {
return IntegrationFlows
.from("FamilyChannel")
.handle(familyNumberMessageSource(), "receive")
.get();
}
}

Related

Spring Integration Wiretap with Annotations (4.3 Spring Integration)

I have a Spring Integration (4.3) application and i have a JMS receiver which sends the received message to a ServiceActivator.
I am trying to Wiretap a channel, and send the JMS message to Logger (which in this case is Service Activator as well)
I get a strange behavior with the code below.
Every odd message sent (first, third) - it only goes to the logger.
Every even message sent (second, fourth, etc) - only goes to the ServiceActivator that is supposed to do some work and in the future sent the message downstream.
Here are logs
2018-07-23 16:14:43.278 INFO 16532 --- [ container-1] zzz : MSG1
Received via barChannel: MSG2
How can i change my code so all messages go both to the logger and the ServiceActivator that is supposed to do the work?
Thanks a lot in advance
#Bean
public ConnectionFactory jmsConnectionFactory() {
return new ActiveMQConnectionFactory("tcp://localhost:61616");
}
#Bean
public JmsMessageDrivenEndpoint inbound() {
JmsMessageDrivenEndpoint jmsMessageDrivenEndpoint = new JmsMessageDrivenEndpoint(container(), listener());
jmsMessageDrivenEndpoint.setSessionAcknowledgeMode("transacted");
jmsMessageDrivenEndpoint.setAutoStartup(true);
return jmsMessageDrivenEndpoint;
}
#Bean
public DefaultMessageListenerContainer container() {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(jmsConnectionFactory());
container.setDestinationName("foo.bar");
return container;
}
#Bean
public ChannelPublishingJmsMessageListener listener() {
ChannelPublishingJmsMessageListener listener = new ChannelPublishingJmsMessageListener();
listener.setRequestChannelName("myLogChannel");
return listener;
}
#ServiceActivator(inputChannel = "myLogChannel")
public void bar(String in) {
System.out.println("Received via barChannel: " + in);
}
#Bean
public MessageChannel TappingChannel(MessageChannel myLogChannel) {
DirectChannel d = new DirectChannel();
d.addInterceptor(new WireTap("myLogChannel"));
return d;
}
#Bean
#ServiceActivator(inputChannel = "myLogChannel")
public MessageHandler logger() {
LoggingHandler loggingHandler = new LoggingHandler(LoggingHandler.Level.INFO.name());
loggingHandler.setLoggerName("zzz");
return loggingHandler;
}
#Bean
public MessageChannel myLogChannel() {
return new DirectChannel();
}
EDIT: Here is the original code that works
#Bean
public MessageChannel toRouter() {
return new DirectChannel();
}
#Bean
public ConnectionFactory jmsConnectionFactory() {
return new ActiveMQConnectionFactory("tcp://localhost:61616");
}
#Bean
public JmsMessageDrivenEndpoint inbound(ConnectionFactory jmsConnectionFactory) {
return new JmsMessageDrivenEndpoint(container(jmsConnectionFactory), listener());
}
#Bean
public DefaultMessageListenerContainer container(ConnectionFactory jmsConnectionFactory) {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(jmsConnectionFactory);
container.setDestinationName("foo.bar");
return container;
}
#Bean
public ChannelPublishingJmsMessageListener listener() {
ChannelPublishingJmsMessageListener listener = new ChannelPublishingJmsMessageListener();
listener.setRequestChannelName("toRouter");
return listener;
}
#ServiceActivator(inputChannel = "toRouter")
public void bar(String in) {
System.out.println("Received via barChannel: " + in);
}
I want to do the Wiretap logging on this code
You have a mess in your config.
First of all you have two #ServiceActivator for the same myLogChannel DirectChannel.
By default it uses a round-robin dispatching strategy. That's why you see that odd/even behavior between your two consumers on the same channel.
You separate TappingChannel doesn't bring any value and it is out of use at all. No one sends message to this channel: you send to the listener.setRequestChannelName("myLogChannel"); directly though... Therefore a WireTap isn't performed at all as well.
I'm not sure how to help you since it isn't clear what you are going to use as a main channel and what should be used from the wire-tap.
UPDATE
So, you need to configure that new WireTap("myLogChannel") on this toRouter channel.

ServiceActivator does not receive message from ImapIdleChannelAdapter

ServiceActivator does not receive messages from ImapIdleChannelAdapter...
JavaMail logs successful FETCH, but MIME messages do not get delivered to SA endpoint... I want to understand what is wrong in my code.
A7 FETCH 1:35 (ENVELOPE INTERNALDATE RFC822.SIZE FLAGS BODYSTRUCTURE)
* 1 FETCH (ENVELOPE ("Fri....
Code snippet below:
`
#Autowired
EmailConfig emailCfg;
#Bean
public SubscribableChannel mailChannel() {
return MessageChannels.direct().get();
}
#Bean
public ImapIdleChannelAdapter getMailAdapter() {
ImapMailReceiver mailReceiver = new ImapMailReceiver(emailCfg.getImapUrl());
mailReceiver.setJavaMailProperties(javaMailProperties());
mailReceiver.setShouldDeleteMessages(false);
mailReceiver.setShouldMarkMessagesAsRead(true);
ImapIdleChannelAdapter imapIdleChannelAdapter = new ImapIdleChannelAdapter(mailReceiver);
imapIdleChannelAdapter.setOutputChannel(mailChannel());
imapIdleChannelAdapter.setAutoStartup(true);
imapIdleChannelAdapter.afterPropertiesSet();
return imapIdleChannelAdapter;
}
#ServiceActivator(inputChannel = "mailChannel")
public void receive(String mail) {
log.warn(mail);
}
private Properties javaMailProperties() {
Properties javaMailProperties = new Properties();
javaMailProperties.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
javaMailProperties.setProperty("mail.imap.socketFactory.fallback", "false");
javaMailProperties.setProperty("mail.store.protocol", "imaps");
javaMailProperties.setProperty("mail.debug", "true");
javaMailProperties.setProperty("mail.imap.ssl", "true");
return javaMailProperties;
}
`
The problem was due to wrong bean initialization. Full version that works OK:
#Slf4j
#Configuration
#EnableIntegration
public class MyMailAdapter {
#Autowired
EmailConfig emailCfg;
#Bean
public SubscribableChannel mailChannel() {
log.info("Channel ready");
return MessageChannels.direct().get();
}
#Bean
public ImapMailReceiver receiver() {
ImapMailReceiver mailReceiver = new ImapMailReceiver(emailCfg.getImapUrl());
mailReceiver.setJavaMailProperties(javaMailProperties());
mailReceiver.setShouldDeleteMessages(false);
mailReceiver.setShouldMarkMessagesAsRead(true);
return mailReceiver;
}
#Bean
public ImapIdleChannelAdapter adapter() {
ImapIdleChannelAdapter imapIdleChannelAdapter = new ImapIdleChannelAdapter(receiver());
imapIdleChannelAdapter.setOutputChannel(mailChannel());
imapIdleChannelAdapter.afterPropertiesSet();
return imapIdleChannelAdapter;
}
#ServiceActivator(inputChannel = "mailChannel")
public void receive(Message<MimeMessage> mail) throws MessagingException {
log.info(mail.getPayload().toString());
}
private Properties javaMailProperties() {
Properties javaMailProperties = new Properties();
javaMailProperties.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
javaMailProperties.setProperty("mail.imap.socketFactory.fallback", "false");
javaMailProperties.setProperty("mail.store.protocol", "imaps");
javaMailProperties.setProperty("mail.debug", "true");
javaMailProperties.setProperty("mail.imap.ssl", "true");
return javaMailProperties;
}
}
I don't know what's exactly wrong with your code but I will suggest you few approches that could help you.
Firstly I suggest you to use java DSL in java based configuration. It will provide you nice way to directly specific flow of your integration application (and avoid simply mistakes). For example for spliiter and service activator:
#Bean
public IntegrationFlow yourFlow(AbstractMessageSplitter splitter,
MessageHandler handler) {
return
IntegrationFlows
.from(CHANNEL)
.split(splitter)
.handle(handler).get();
}
Secondly it's generally bad idea to directly specify message type to String. Try something like this (why String?):
#ServiceActivator(inputChannel = "mailChannel")
public void receive(Message<?> message) {
/* (String) message.getPayload() */
}
Maybe it's not a case but let's check it.

How to configure a trigger in spring integration flow to get value from a method invoking message source?

How to configure a trigger in spring integration flow to get value from a method invoking message source and start it in another flow ?
#Bean
public IntegrationFlow integrationFlow() {
return IntegrationFlows.from(messageSource,channelSpec -> channelSpec.poller(Pollers.trigger(new SomeTrigger())).handle(...).get()
}
#Bean
public MessageSource<?> messageSource() {
MethodInvokingMessageSource source = new MethodInvokingMessageSource();
source.setObject(new Random());
source.setMethod("nextInt");
}
#Bean
public IntegrationFlow someOtherFlow() {
return IntegrationFlows.from("messageChannel")
///some logic to trigger and get the value of random int
}
The MessageSource has receive() method, so you can do just this:
#Bean
public MessageSource<?> randomIntSource() {
MethodInvokingMessageSource source = new MethodInvokingMessageSource();
source.setObject(new Random());
source.setMethodName("nextInt");
return source;
}
#Bean
public IntegrationFlow someOtherFlow() {
return IntegrationFlows.from("messageChannel")
.handle(randomIntSource(), "receive")
.handle(System.out::println)
.get();
}
pay attention to the .handle(randomIntSource(), "receive").

Managing Multiple Cassandra Sessions

I have multiple Cassandra clusters. Each cluster has specific set of contact points. Each cluster has separate set of tables/CF.
In my C* client I am supposed to query both clusters. I am using spring-boot version of the Cassandra. I am trying to use CassandraOperations to do the queries. How do I go about doing this?
#Bean
public CassandraOperations cassandraTemplate(Session sessionA) throws Exception {
return new CassandraTemplate(sessionA);
}
#Bean
public CassandraMappingContext mappingContext() {
return new BasicCassandraMappingContext();
}
#Bean
public CassandraConverter converter() {
return new MappingCassandraConverter(mappingContext());
}
Above is a example whereby I setup cassandraoperations using sessionA, how about doing the same for sessionB?
At any given time base don the query, it can go to either sessionA or sessionB.
Any pointers are appreciated
Thanks
Found a way to do this by creating separate classes for each cluster manager.
#Component
#Primary
public class CassandraTemplateA extends CassandraTemplate{
#Autowired
public CassandraTemplateA(CassandraConverter converter) {
super(sessionA, converter);
}
}
#Component
#Primary
public class CassandraTemplateB extends CassandraTemplate{
#Autowired
public CassandraTemplateB(CassandraConverter converter) {
super(sessionB, converter);
}
}

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());
}
}
}

Resources