I want to rewrite folowing xml configured example using java DSL:
For now I stuck on following part of configuration:
<int:gateway id="userGateway" default-request-timeout="5000"
default-reply-timeout="5000"
service-interface="org.springframework.integration.samples.enricher.service.UserService">
<int:method name="findUser" request-channel="findUserEnricherChannel"/>
<int:method name="findUserByUsername" request-channel="findUserByUsernameEnricherChannel"/>
<int:method name="findUserWithUsernameInMap" request-channel="findUserWithMapEnricherChannel"/>
</int:gateway>
I tried to find any analog here But I haven't found.
Could you please help me to find it?
I tried to write following code:
config:
@Bean
public IntegrationFlow findUserEnricherChannelFlow() {
return IntegrationFlows.from(UserService.class)
.handle(SystemService.class, "findUser") // I have no idea how to map all methods
.get();
}
Invocator:
ConfigurableApplicationContext ctx = new SpringApplication(MyApplication.class).run(args);
UserService userService = ctx.getBean(UserService.class);
User user = new User("some_name",null,null);
System.out.println("Main:" + userService.findUser(user));
UserService:
public interface UserService {
/**
* Retrieves a user based on the provided user. User object is routed to the
* "findUserEnricherChannel" channel.
*/
//@Gateway(requestChannel = "findUserEnricherChannel", replyChannel = )
User findUser(User user);
/**
* Retrieves a user based on the provided user. User object is routed to the
* "findUserByUsernameEnricherChannel" channel.
*/
User findUserByUsername(User user);
/**
* Retrieves a user based on the provided username that is provided as a Map
* entry using the mapkey 'username'. Map object is routed to the
* "findUserWithMapChannel" channel.
*/
Map<String, Object> findUserWithUsernameInMap(Map<String, Object> userdata);
}
systemService:
public class SystemService {
private static final Log LOGGER = LogFactory.getLog(SystemService.class);
/** Default Constructor. */
public SystemService() {
super();
}
public User findUser(User user) {
LOGGER.info(String.format("Calling method 'findUser' with parameter %s", user));
final User fullUser = new User(user.getUsername(),
"secret",
user.getUsername() + "@springintegration.org");
return fullUser;
}
public User findUserByUsername(String username) {
LOGGER.info(String.format("Calling method 'findUserByUsername' with parameter: %s", username));
return new User(username, "secret", username + "@springintegration.org");
}
}
But at this case application can't start with following trace:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-08-29 16:04:54.293 ERROR 7948 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'findUserEnricherChannelFlow' defined in class path resource [enricher/Config.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.integration.dsl.IntegrationFlow]: Factory method 'findUserEnricherChannelFlow' threw exception; nested exception is java.lang.IllegalStateException: Target object of type [class java.lang.Class] has no eligible methods for handling Messages.
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:627) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:456) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1321) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1160) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:845) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:742) ~[spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:389) ~[spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:311) ~[spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
at enricher.MyApplication.main(MyApplication.java:13) ~[classes/:na]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.integration.dsl.IntegrationFlow]: Factory method 'findUserEnricherChannelFlow' threw exception; nested exception is java.lang.IllegalStateException: Target object of type [class java.lang.Class] has no eligible methods for handling Messages.
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
... 17 common frames omitted
Caused by: java.lang.IllegalStateException: Target object of type [class java.lang.Class] has no eligible methods for handling Messages.
at org.springframework.util.Assert.state(Assert.java:94) ~[spring-core-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.integration.handler.support.MessagingMethodInvokerHelper.findHandlerMethodsForTarget(MessagingMethodInvokerHelper.java:898) ~[spring-integration-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.integration.handler.support.MessagingMethodInvokerHelper.<init>(MessagingMethodInvokerHelper.java:293) ~[spring-integration-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.integration.handler.support.MessagingMethodInvokerHelper.<init>(MessagingMethodInvokerHelper.java:223) ~[spring-integration-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.integration.handler.support.MessagingMethodInvokerHelper.<init>(MessagingMethodInvokerHelper.java:227) ~[spring-integration-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.integration.handler.MethodInvokingMessageProcessor.<init>(MethodInvokingMessageProcessor.java:54) ~[spring-integration-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.integration.handler.ServiceActivatingHandler.<init>(ServiceActivatingHandler.java:46) ~[spring-integration-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.integration.dsl.IntegrationFlowDefinition.handle(IntegrationFlowDefinition.java:996) ~[spring-integration-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.integration.dsl.IntegrationFlowDefinition.handle(IntegrationFlowDefinition.java:979) ~[spring-integration-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at enricher.Config.findUserEnricherChannelFlow(Config.java:36) ~[classes/:na]
at enricher.Config$$EnhancerBySpringCGLIB$$f36636fe.CGLIB$findUserEnricherChannelFlow$0(<generated>) ~[classes/:na]
at enricher.Config$$EnhancerBySpringCGLIB$$f36636fe$$FastClassBySpringCGLIB$$ada0b78a.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at enricher.Config$$EnhancerBySpringCGLIB$$f36636fe.findUserEnricherChannelFlow(<generated>) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
... 18 common frames omitted
I made several steps ahead and for now I have following config:
@Configuration
@EnableIntegration
@IntegrationComponentScan
public class Config {
@Bean
public SystemService systemService() {
return new SystemService();
}
@Bean
public IntegrationFlow findUserEnricherFlow(SystemService systemService) {
return IntegrationFlows.from("findUserEnricherChannel")
.<User>handle((p, h) -> systemService.findUser(p))
.get();
}
@Bean
public IntegrationFlow findUserByUsernameEnricherFlow(SystemService systemService) {
return IntegrationFlows.from("findUserByUsernameEnricherChannel")
.<User>handle((p, h) -> systemService.findUserByUsername(p.getUsername()))
.get();
}
@Bean
public IntegrationFlow findUserWithUsernameInMapFlow(SystemService systemService) {
return IntegrationFlows.from("findUserWithMapEnricherChannel")
.<Map<String, Object>>handle((p, h) -> {
User user = systemService.findUserByUsername((String) p.get("username"));
Map<String, Object> map = new HashMap<>();
map.put("username", user.getUsername());
map.put("email", user.getEmail());
map.put("password", user.getPassword());
return map;
})
.get();
}
}
service interface:
@MessagingGateway
public interface UserService {
/**
* Retrieves a user based on the provided user. User object is routed to the
* "findUserEnricherChannel" channel.
*/
@Gateway(requestChannel = "findUserEnricherChannel")
User findUser(User user);
/**
* Retrieves a user based on the provided user. User object is routed to the
* "findUserByUsernameEnricherChannel" channel.
*/
@Gateway(requestChannel = "findUserByUsernameEnricherChannel")
User findUserByUsername(User user);
/**
* Retrieves a user based on the provided username that is provided as a Map
* entry using the mapkey 'username'. Map object is routed to the
* "findUserWithMapChannel" channel.
*/
@Gateway(requestChannel = "findUserWithMapEnricherChannel")
Map<String, Object> findUserWithUsernameInMap(Map<String, Object> userdata);
}
and target service:
public class SystemService {
private static final Log LOGGER = LogFactory.getLog(SystemService.class);
/** Default Constructor. */
public SystemService() {
super();
}
public User findUser(User user) {
LOGGER.info(String.format("Calling method 'findUser' with parameter %s", user));
final User fullUser = new User(user.getUsername(),
"secret",
user.getUsername() + "@springintegration.org");
return fullUser;
}
public User findUserByUsername(String username) {
LOGGER.info(String.format("Calling method 'findUserByUsername' with parameter: %s", username));
return new User(username, "secret", username + "@springintegration.org");
}
}
main method:
public static void main(String[] args) {
ConfigurableApplicationContext ctx = new SpringApplication(MyApplication.class).run(args);
UserService userService = ctx.getBean(UserService.class);
User user = new User("some_name", null, null);
System.out.println("Main:" + userService.findUser(user));
System.out.println("Main:" + userService.findUserByUsername(user));
Map<String, Object> map = new HashMap<>();
map.put("username", "vasya");
System.out.println("Main:" + userService.findUserWithUsernameInMap(map));
}
output:
2019-08-30 14:09:29.956 INFO 12392 --- [ main] enricher.MyApplication : Started MyApplication in 2.614 seconds (JVM running for 3.826)
2019-08-30 14:09:29.966 INFO 12392 --- [ main] enricher.SystemService : Calling method 'findUser' with parameter User{username='some_name', password='null', email='null'}
Main:User{username='some_name', password='secret', email='some_name@springintegration.org'}
2019-08-30 14:09:29.967 INFO 12392 --- [ main] enricher.SystemService : Calling method 'findUserByUsername' with parameter: some_name
Main:User{username='some_name', password='secret', email='some_name@springintegration.org'}
2019-08-30 14:09:29.967 INFO 12392 --- [ main] enricher.SystemService : Calling method 'findUserByUsername' with parameter: vasya
Main:{password=secret, email=vasya@springintegration.org, username=vasya}
As you can see everything is working properly but I do transformations inside the configuration. I am not sure if I have to do it because xml configuration dooesn't have such transformations and everything somehow works using internal magic. Is it correct way or should I use some internal DSL magic for transformations?
That documentation is for an old version of the DSL for Spring Integration 4.3; since version 5.0, the DSL is integrated into the main project and documentation. The gateway feature did not exist in that version.
See the banner at the top of the Wiki page
This project has been absorbed by Spring Integration Core starting with version 5.0. Please consult its Reference Manual for the actual documentation. This project is only in a maintenance, bug fixing state.
See the current documentation - IntegrationFlow as Gateway.
The IntegrationFlow can start from the service interface that provides a GatewayProxyFactoryBean component, as the following example shows:
public interface ControlBusGateway {
void send(String command);
}
...
@Bean
public IntegrationFlow controlBusFlow() {
return IntegrationFlows.from(ControlBusGateway.class)
.controlBus()
.get();
}
It works the same for that sample app
public interface RequestGateway {
String echo(String request);
}
....from(RequestGateway.class)
...
EDIT
The DSL gateway currently doesn't support multi-method gateways with different channels. You can use a couple of techniques...
@SpringBootApplication
@IntegrationComponentScan // finds the messaging gateway
public class So57709118Application {
public static void main(String[] args) {
SpringApplication.run(So57709118Application.class, args);
}
@Bean
public IntegrationFlow flow() {
return IntegrationFlows.from("gatewayChannel")
//.route(...)
.log()
.nullChannel();
}
@Bean
@DependsOn("flow")
public ApplicationRunner runner(Gate gate) {
return args -> {
gate.method1("bar");
gate.method2("bar");
};
}
}
@MessagingGateway(defaultRequestChannel = "gatewayChannel",
defaultHeaders = @GatewayHeader(name = "method", expression = "#gatewayMethod.name"))
interface Gate {
void method1(String foo);
void method2(String foo);
}
Or, instead of a common flow for all, you can set the requestChannel on each method
@MessagingGateway(defaultHeaders = @GatewayHeader(name = "method", expression = "#gatewayMethod.name"))
interface Gate {
@Gateway(requestChannel = "foo")
void method1(String foo);
@Gateway(requestChannel = "bar")
void method2(String foo);
}