spring-integration-java-dsl channel creation - spring-integration

doc says : The new DirectChannel bean will be created on context startup if there is no bean with this name.
#MessagingGateway
public interface Responder {
#Gateway(requestChannel = "request.input")
String respond(String request);
}
#Bean
public IntegrationFlow doResponse(){
return IntegrationFlows
.from("request.input")
.transform("payload")
.get();
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {IntegrationConfiguration.class})
public class GatewayTests {
#Autowired Responder responder;
#Test
public void test(){
responder.respond("request");
}
}
this results in : No bean named 'request.input' is defined
have i left something out which triggers the channel creation ?
spring-boot : 1.1.9.RELEASE
spring-integration-java-dsl : 1.0.0.RELEASE
spring-integration : 4.0.4.RELEASE

You should be sure that Spring Integration infrastructure is swtiched on: #EnableIntegration on the #Configuration class, or #EnableAutoConfiguration if you use Spring Boot.
From other side not all auto-channel features works with Spring Integration 4.0.x.
With that you always can overcome the issue with explicit MessageChannel #Bean.

Related

Not able insert through jpa outbound gateway

#Bean
public IntegrationFlow reimInvJpaOutbound() {
return IntegrationFlows
.from("reimInvProcessChannel")
.handle(reimJpaHandlers.reimStgInsertJpaHandler())
.log()
.get();
}
#Component
#Transactional
public class ReIMJpaHandlers {
Logger logger = LoggerFactory.getLogger(this.getClass());
#PersistenceContext
protected EntityManager entityManager;
#Autowired
ReIMHistInvHdrStgRepository histRepo;
#Autowired
ReIMInvHdrStgRepository stgRepo;
#Autowired
ReIMErrInvHdrStgRepository errRepo;
String responseQueryString = "select * from RMS16DEV.TSC_IM_DOC_HEAD_TEMP where error_ind != null";
#Bean
public JpaUpdatingOutboundEndpointSpec reimStgInsertJpaHandler() {
System.out.println("Writing to reim stg");
return Jpa
.updatingGateway(entityManager)
.entityClass(TSC_IM_DOC_HEAD_TEMP.class)
;
}
#Bean
public JpaPollingChannelAdapter reimStgResponseJpaInboundAdapter() {
return Jpa
.inboundAdapter(entityManager)
.nativeQuery(responseQueryString)
.maxResults(100)
.get();
}
}
But I am getting below error:
javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'merge' call
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:292) ~[spring-orm-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at com.sun.proxy.$Proxy189.merge(Unknown Source) ~[na:na]
Your
#Component
#Transactional
public class ReIMJpaHandlers {
doesn't matter for the
#Bean
public JpaUpdatingOutboundEndpointSpec reimStgInsertJpaHandler() {
The last one is a bean and it lives in its own lifecycle and all its method calls are happened already outside of your #Transactional on the ReIMJpaHandlers.
You need to consider to configure a TX manager exactly for the .handle(reimJpaHandlers.reimStgInsertJpaHandler()):
.handle(reimJpaHandlers.reimStgInsertJpaHandler(), e -> e.transactional())
assuming that you have a bean with the transactionManager name.
The #Transactional on the class is applied for the business methods, but not #Bean methods which are called only once when we create a bean.

Error while unit testing spring integration dsl

I am seeing following error while running the unit testing my spring integration code. And also i was not sure how to mock my service and other dependencies while using mockIntegrationContext.
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'orderInputEndPoint' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:686)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1210)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204)
Main program code
#EnableIntegration
public class OrderPersistFlow {
#Autowired
private ActiveMQConnectionFactory activeMQConnectionFactory;
#Autowired
private OrderTransformer orderTransformer;
#Autowired
private OrderService orderService;
#Bean
public IntegrationFlow persistFlow() {
return IntegrationFlows
.from(Jms.messageDrivenChannelAdapter(activeMQConnectionFactory)
.id("orderInputEndPoint")
.destination("order.queue")
.jmsMessageConverter(new MarshallingMessageConverter(jaxbMarshaller())))
.filter(OrderVO.class, p -> p.getOrderStatus().equals("OPEN")
.transform(orderTransformer)
.handle(orderService, "save")
.get();
}
}
Test code
RunWith(SpringRunner.class)
#SpringIntegrationTest(noAutoStartup = "orderInputEndPoint")
public class OrderPersistFlowTest {
#Autowired
private MockIntegrationContext mockIntegrationContext;
#Test
public void persistFlowTest(){
OrderVO orderVO = new OrderVO();
orderVO.setId("1234");
orderVO.setName("TestOrder");
orderVO.setDescription("order desc");
MessageSource<OrderVO> messageSource = () -> new GenericMessage<>(orderVO);
this.mockIntegrationContext.substituteMessageSourceFor("orderInputEndPoint", messageSource);
Message<?> receive = messageSource.receive();
}
}
I don't see a #ContextConfiguration(classes = OrderPersistFlow.class) on your test class: https://docs.spring.io/spring/docs/5.0.6.RELEASE/spring-framework-reference/testing.html#integration-testing-annotations-spring
Fully not clear what you are going to test then if there is no application context to load...

spring integration sftp error: Dispatcher has no subscribers

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().

How to use AOP on spring integration gateways?

I would like to intercept all spring integration gateways via AOP.
Is it possible to do that? If not what might be best way to do log input object coming to gateway?
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#DirtiesContext
public class AdviceExample {
#Autowired
private TestGateway testGateway;
#Test
public void testIt() {
System.out.println(this.testGateway.testIt("foo"));
}
#MessagingGateway
public interface TestGateway {
#Gateway(requestChannel = "testChannel")
#CustomAnnotation
String testIt(String payload);
}
#Configuration
#EnableIntegration
#IntegrationComponentScan
#EnableMessageHistory
#EnableAspectJAutoProxy
public static class ContextConfiguration {
LoggingHandler logger = new LoggingHandler(LoggingHandler.Level.INFO.name());
#Bean
public IntegrationFlow testFlow() {
return IntegrationFlows.from("testChannel")
.transform("payload.toUpperCase()")
.channel("testChannel")
.transform("payload.concat(' Manoj')")
.channel("testChannel")
.handle(logger)
.get();
}
#Bean
public GatewayAdvice gtwyAdvice(){
return new GatewayAdvice();
}
}
#Retention(value = RetentionPolicy.RUNTIME)
#Target(value = ElementType.METHOD)
#Inherited
public #interface CustomAnnotation{
}
#Aspect
public static class GatewayAdvice {
#Before("execution(* advice.AdviceExample.TestGateway.testIt(*))")
public void beforeAdvice() {
System.out.println("Before advice called...");
}
#Before("#annotation(advice.AdviceExample.CustomAnnotation)")
public void beforeAnnotationAdvice() {
System.out.println("Before annotation advice called...");
}
}
}
Yes, you can do that. Take a look to the standard Spring AOP Framework. Since all those #Gateway are beans in the end you can add for them any Advice by their bean names and for the specific method, if that. For example we often suggest to use #Transactional on gateway's methods. And this is exactly a sample "how to use AOP on integration gateway".

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