Gateway method-name as header value - spring-integration

I have a Gateway with a default-request-channel an multiple methods.
public interface IUserService {
public void updateUserName(Long id, String username);
public void updatePassword(Long id, String password);
...
}
and the following xml config
...
<gateway id="userService" service-interface="...IUserService"
default-request-channel="dataRequestChannel"
default-reply-channel="dataResponseChannel" />
...
How can i get information about the method which is invoked ?
I know that it is possible to apply static header values but is that the only way ?
Or am i totally wrong ?
Thanks

We have an open JIRA issue for this feature; please vote it up.
Right now, the #method variable is available in expressions within specific method declarations
<int:gateway id="gw"
service-interface="foo.Gateway"
default-request-channel="input">
<int:method name="sendAndReceive">
<int:header name="gwMethod" expression="#method"/>
</int:method>
</int:gateway>
But you would still have to declare each method.
Perhaps another, relatively simple enhancement would be to support wildcards in method names; something like...
<int:gateway id="gw"
service-interface="foo.Gateway"
default-request-channel="input">
<int:method name="*">
<int:header name="gwMethod" expression="#method"/>
</int:method>
</int:gateway>
Where headers for method * would be added for all methods.

As of 3.0.1 you can do the following to set headers across all methods accessed via the gateway (you can also manage the specific methods as usual).
I am showing how to set two header properties, transportType and methodType, one dynamic and one static:
<int:gateway id="gw"
service-interface="foo.async.Gateway"
default-request-channel="gateway-request-channel"
default-reply-channel="gateway-response-channel">
<int:default-header name="transportType" value="async-msg"/>
<int:default-header name="methodType" expression="#gatewayMethod.name"/>
</int:gateway>
http://docs.spring.io/spring-integration/reference/html/messaging-endpoints-chapter.html#gateway-configuration-annotations
A bit late but the correct solution for those following this thread later.

Related

How to declare reusable transformer in XML without 'input-channel'?

In my application I would like to re-use the same message transformer inside of multiple <int:chain>.
In such chains I perform http requests to different endpoints and I need to add the same basic authentication header. I would like to declare the code for adding a header only once, i.e:
<int:header-enricher id="authHeaderAdder">
<int:header expression="'Basic ' + new String(T(java.util.Base64).encoder.encode(('${http.user}' + ':' + '${http.password}').bytes))"
name="Authorization"/>
</int:header-enricher>
And then I would like to use it with ref in my chain before making http request:
<int:chain input-channel="someHttpChain">
<int:transformer ref="authHeaderAdder"/>
<http:outbound-gateway.../>
<int:transformer ref="someResponseTransformer"/>
</int:chain>
The problem is that I get an error on application startup:
Configuration problem: The 'input-channel' attribute is required for the top-level endpoint element: 'int:header-enricher' with id='authHeaderAdder'
How can I re-use authHeaderAdder without writing any java code and making a <bean/>?
You definitely need to use an input-channel on that <int:header-enricher>, e.g. input-channel="authChannel" but inside the <chain> you get a gain to use something like <int:gateway request-channel="authChannel"/>. That's all: you are reusing the same transformer, but via the Spring Integration trick with the MessageChannel.
Such an approach is cool the way that you can add more endpoint in that authChannel flow without any changes in the original flow where you use that gateway.

Spring Integration - how to pass parameters into the method of service activator

I need to pass the parameter in the method called via service-activator. I am able to successfully do this with the help of header-enricher. Below is the working code snippet.
<int:chain input-channel="inChannel">
<int:header-enricher>
<int:header name="routeName" value="TestRoute" />
</int:header-enricher>
<int:service-activator ref="customLoggingRoute"
method="logRoute">
</int:service-activator>
</int:chain>
public Message logRoute(Message m, #Header("routeName") String routeName) {
System.out.println("Inside route: " + routeName);
return m;
}
But I dont want to add anything to header. Is there any alternative by which we can accomplish the same thing without header-enricher.
No, there is no. The Spring Integration contract for invokers is Messaging. So, when we have only a message in between, there is nothing more we can utilize. You can have a complex payload object and transfer data through it, or move your data into headers.
Of course you can consider to use a ThreadLocal since we are in Java and as long as your flow is in the same thread, but this is going to be slightly overhead.
I think you need to start from the theory of the Messaging and come back to us when you understand all those restrictions.
https://www.enterpriseintegrationpatterns.com/

Spring Integration : How to intercept ALL spring integration component and fetch attribute values to log

I need to intercept ALL the Spring Integration components at runtime and should be able to fetch the attribute values in order to log a meaningful message.
For example:
<int-http:outbound-gateway url="someURL" http-method="GET"
request-channel="channel1"
expected-response-type="com.example.Test"
message-converters="customMessageConverters">
<int-http:uri-variable name="testId" expression="headers.testId"/>
</int-http:outbound-gateway>
In the example above, I need to intercept int-http:outbound-gateway and capture the value for url, request-channel and expected-response-type. We need to do this for all http outbound gateway.
Similarly, for all other components like int-http:inbound-gateway, int-http:inbound-channel-adapter, int:transformer, int:header-enricher, int:chain, int:router, etc.
I have tried creating a custom class implementing BeanPostProcessor - postProcessAfterInitialization method. Checked for the bean name to be matching with the component, and tried to retrieve all the details but the beans are created and this method is called at the server startup itself. My requirement is to capture the flow as and when the user navigates and any particular route is being called. Also I am not able to find Java class name for all the component apart from the below. Still finding for the rest.
org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway for int-http:inbound-gateway, org.springframework.integration.http.outbound.AbstractHttpRequestExecutingMessageHandler for int-http:outbound-gateway
Update:
I have tried the below but cant see any extra output in logs with respect to message history. Is anything missing in the above code?
<int:message-history />
<int:logging-channel-adapter id="logging"
log-full-message="true" logger-name="message.history" level="DEBUG"/>
<int:wire-tap pattern="*" order="3" channel="logging" />
or
<int:message-history />
<int:logging-channel-adapter id="logger"
log-full-message="true" logger-name="message.history" level="DEBUG"/>
<int:channel id="wiretapChannel">
<int:interceptors>
<int:wire-tap channel="logger"/>
</int:interceptors>
</int:channel>
Also, I am trying to inject LogMessage into wire-tap inorder to perform some additional tasks from MessageHistory data. But the control doesn't enter handleMessage method. Please help.
<bean id="logMessage" class="com.logging.LogMessage"/>
<int:service-activator input-channel="wiretapChannel" ref="logMessage" method="handleMessage"></int:service-activator>
public class LogMessage {
public void handleMessage(org.springframework.messaging.Message<?> message) throws MessagingException {
MessageHistory history = MessageHistory.read(message);
for (int i = 0; i < history.size(); i++) {
Properties properties = history.get(i);
getLogger().info("history: " + properties.get("name"));
}
}
}
Well, it's not so standard task, especially for the reading properties you would like to print. More over many of them are based on the SpEL expressions and the actual value depends on the request message.
There is though a component which is very close what you would like to get. It is called Message History.
All what you need is <int:message-history/> - and all the Spring Integration components will be tracked and will store they point into the MessageHistory.HEADER_NAME to show the whole path of the message over integration flow.
In addition I usually also use something like this:
<wire-tap channel="logger"/>
<logging-channel-adapter id="logger" log-full-message="true" logger-name="message.history"/>
To intercept all the channels in the application and log messages with their message history.
You may create your own POJO subscriber (<service-activator>) for the global <wire-tap> and perform some smart logic to extract a MessageHistory from the message: MessageHistory.read(Message<?>). Such a MessageHistory is a List<Properties> where, actually you can cast that Properties in the MessageHistory.Entry class and walk over its properties:
public String getName() {
return this.getProperty(NAME_PROPERTY);
}
public String getType() {
return this.getProperty(TYPE_PROPERTY);
}
public String getTimestamp() {
return this.getProperty(TIMESTAMP_PROPERTY);
}
With the name you can go to the BeanFactory to get the real component instance and already there try to extract required properties for your purpose, but again: not all of them are going to be available just because...
You also can consult with the Integration Graph for possible public properties of the IntegrationNode implementations.

Message not available at end of Aggregator

I have built a Spring Integration application and transferred some messages around and tried to bring them together with an Aggregator. The application reaches the Aggregator but does not deliver exactly what I want specifically I do not release the group and move onto the next step.
My problem however is my aggregator doesn't have the original message (from before the Splitter). My aggregator is defined as follows
<int:aggregator input-channel="deirBoxProcessorToAggregatorChannel"
ref="loggingAggregator" method="logAggregation"
output-channel="aggregatorToTransformer"
expire-groups-upon-completion="true"/>
And the code inside it is as follows..
public class LoggingAggregator {
private static final Logger LOGGER = Logger.getLogger(LoggingAggregator.class);
public void logAggregation(Message<File> message) {
LOGGER.info("Have aggregated messsages. Will archive");
}
My message in that method, although it enters it, is always null.
Application Context/XML Spring Integration definition
<int:splitter input-channel="transformerToSplitterChannel"
ref="fileToMessageSplitter"
output-channel="shippedSplitterToRouterChannel"
method="split" apply-sequence="true"/>
<!-- Now use a router to determine which Message builder these messages are sent onto -->
<int:router input-channel="shippedSplitterToRouterChannel"
ref="shippedToTypeRouter" />
<int:transformer input-channel="deirShippedBoxToTransformerChannel"
ref="shippedBoxTransformer" method="transform" output-
channel="deirShippedTransformerToProcessorChannel"/>
<int:service-activator id="wellFormedShippedBoxProcess"
input-channel="deirShippedTransformerToProcessorChannel"
output-channel="deirBoxProcessorToAggregatorChannel"
ref="deirShippedFileProcessor" method="processBox" />
<int:service-activator id="malformedShippedBoxProcess"
input-channel="deirMalformedShippedTransformerToProcessorChannel"
output-channel="deirBoxProcessorToAggregatorChannel"
ref="deirShippedFileProcessor"
method="processMalformedBox" />
<int:aggregator input-channel="deirBoxProcessorToAggregatorChannel"
ref="loggingAggregator" method="logAggregation"
output-channel="aggregatorToTransformer"
expire-groups-upon-completion="true"/>
<int:transformer expression="headers.file_originalFile"
input-channel="aggregatorToTransformer"
output-channel="transformerToArchiver" />
<int-file:outbound-channel-adapter id="deirArchiver"
channel="transformerToArchiver"
directory="${dataexhange.springintg.refactor.archive.dir}"
delete-source-files="true"/>
The process gets all the way to the Aggregator but does not seem to make it past to the Transformer or OutboundChannelAdapter archiver.
Thank you in advance.
Your LoggingAggregator isn't correct. I recommend you to read the Reference Manual.
Your logAggregation method should be like this:
public File logAggregation(List<String> lines) {
LOGGER.info("Have aggregated messsages. Will archive");
// Create Files from lines
return file;
}
It is a main method of Aggregator: to get a list of objects and return one object.
Artem's answer is correct. I mistakenly thought that the objects I returned to the aggregator would be of type that were sent off by the splitter. You can follow how through debugging I came to that realisation in the comments to Artem's answer.
I did see somewhere, probably in the manual you can in fact return a type that can be cast from the channel that feeds into the aggregator.
With that understanding I could in fact return Object, and cast back up to the required type for use in the logging object I would use either subsequent to or as part of the aggregator.

How to determine the type of the camel exchange object

I have two different services running on a web server. Both the services have an operation named 'xyz', with the following arguments.
Service 1:
Public String xyx(Student object) {}
Service 2:
public String xyz(Employee object){}
Now i have a client which will invoke the operation of one of these services based on the message that it receives. The message will be received as a camel exchange. So i need to identify the type of the message and then invoke the appropriate service.
How do i identify the original type of the message that is received as a camel exchange.
Thanks.
Or you can do something like this:
from("foo:incommingroute")
.choice()
.when(simple("${body} is 'java.lang.String'"))
.to("webservice:Student")
.when(simple("${body} is 'foo.bar.Employee'"))
.to("webservice:Employee")
.otherwise()
.to("jms:Deadletter")
.end();
Try exchange.getIn().getBody() instanceof Student
I would set the a value in the header to indicate which service it is and then send this off on the camel route. This approach is just but one way of doing this. Christian Schneider has another excellent solution which I will probably use much more now that I have gotten much more into Camel then ever before. However both will achieve the same thing and depending on who you ask one might be more clear than the other.
For example you can do:
public void foo(Exchange exchange){
exchange.getIn().setHeader("MsgType", "Student");
}
You can then filter on the header in either the Java DSL or even spring DSL.
In Java DSL you would do something like this (pseudo code)
from("foo:incommingroute")
.choice()
.when(header("MsgType").equals("Student"))
.to("webservice:Student")
.when(header("MsgType").equals("Employee"))
.to("webservice:Employee")
.otherwise()
.to("jms:Deadletter")
.end();
In Spring DSL you would do something like this (pseudo code)
<route>
<from uri="foo:incommingroute"/>
<choice>
<when>
<simple>${header.MsgType} equals 'Student'</simple>
<to uri="webservice:Student"/>
</when>
<when>
<simple>${header.MsgType} equals 'Employee'</simple>
<to uri="webservice:Employee"/>
</when>
<otherwise>
<to uri="jms:badOrders"/>
<stop/>
</otherwise>
</choice>
<to uri="jms:Deadletter"/>
</route>
You can also look at the enricher pattern at this link http://camel.apache.org/content-enricher.html. Basically what I am suggesting is following the enricher pattern. If you could tell me how you are sending messages to Camel then I could probably help more.
Hope this give you some ideas and if there is syntax mistakes etc in the code sorry I am at a bus stop and did not have time to check it.
I prefer to write this type of logic directly in the route definition rather than in a Processor. Here is the Camel DSL approach that uses a Predicate to determine the body class type. It assumes that you have already deserialized the Exchange body into a Student or Employee object.
choice()
.when(body().isInstanceOf(Student::class))
.to(...)
.when(body().isInstanceOf(Employee::class))
.to(...)
.end()
If you're going to perform various transformations on the body throughout the route, resulting in a variety of Student or Employee object types at various stages (e.g. a Student then a StudentEntity, etc) then saving the type in a header or property as some String constant at the beginning of the route might be the cleaner approach.
// Note that this labelling could be bundled into a processor
choice()
.when(body().isInstanceOf(Student::class))
.setProperty("TYPE", "STUDENT")
.when(body().isInstanceOf(Employee::class))
.setProperty("TYPE", "EMPLOYEE")
.end()
// later after some body transformations
.choice()
.when(exchangeProperty("TYPE").isEqualTo("STUDENT"))
// process student
Lastly, you might be able to do everything in a processor but I think this sort of branch logic combined with service invocation is a Camel anti-pattern.
class MyProcessor implements Processor {
#Override
public void process(Exchange exchange) {
Object body = exchange.getIn().getBody()
if (body instanceOf Student) {
// invoke StudentService
} else if (body instanceOf Employee) {
// invoke EmployeeService
}
}
}
// Route definition
from(...)
.process(myProcessor)

Resources