Is there any way we can use file outbound adapter to write the entire SOAP envelop to file rather than just payload ? (I am able to write the payload into a file).
Assuming you use there <int-ws:inbound-gateway>. Or it doesn't matter for the target solution...
You should implement some EndpointInterceptor add it to the UriEndpointMapping. With the handleRequest implementation you have access to the whole MessageContext where you are able to do something like:
((SoapMessage) messageContext.getRequest()).getEnvelope()
And send this object to the channel for the outbound-channel-adapter to stream its Source to the file.
From the gateway perspective you don't have the access to the messageContext anymore.
UPDATE
According to your comment below (please, consider to place similar info in the question directly in the future), I can suggests something like headers trick:
You implement custom SoapHeaderMapper (extends DefaultSoapHeaderMapper) and overriding toHeadersFromRequest() store passed in SoapMessage to your won header and use it the same way as we discussed in the EndpointInterceptor case. From the <int-file:outbound-channel-adapter> perspective you just should consult that header to extract the Source and its InputStream to store in the file.
UPDATE 2
public class MySoapHeaderMapper extends DefaultSoapHeaderMapper {
#Override
public Map<String, Object> toHeadersFromRequest(SoapMessage source) {
Map<String, Object> headers = super.toHeadersFromRequest(source);
headers.put("soapMessage", source);
return headers;
}
}
You should inject it into <int-ws:inbound-gateway> as a header-mapper. Afterwards that soapMessage header will be available in any downstream component. E.g.
<chain input-channel="storeToFile">
<transformer expression="headers.soapMessage.envelope.source.inputStream"/>
<int-file:outbound-channel-adapter />
</chain>
Related
1) I would like to create a bean of HttpRequestExecutingMessageHandler (outbound channel adapter for HTTP) and specify the channel via an annotation like #OutboundChannelAdapter, why this is not possible? I suppose there is some design decision that I'm not understanding.
2) What is the suggested way of define HttpRequestExecutingMessageHandler without using XML configuration files? Do I have to configure the bean and set it manually?
Thanks in advance.
The #ServiceActivator fully covers that functionality. Unlike #Transformer it doesn't require a return value. So, your POJO method can be just void and the flow is going to stop there similar way a <outbound-channel-adapter> does that in XML configuration.
But in case of HttpRequestExecutingMessageHandler we need to worry about some extra option to make it one-way and stop there without care about any HTTP reply.
So, for the HttpRequestExecutingMessageHandler you need to declare a bean like:
#Bean
#ServiceActivator(inputChannel = )
public HttpRequestExecutingMessageHandler httpRequestExecutingMessageHandler() {
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler();
handler.setExpectReply(false)
return handler;
}
I think we need to improve docs on the matter anyway, but you can take a look into Java DSL configuration instead: https://docs.spring.io/spring-integration/docs/current/reference/html/#http-java-config. There is an Http.outboundChannelAdapter() for convenience.
I have been working on a "paved road" for setting up asynchronous messaging between two micro services using AMQP. We want to promote the use of separate domain objects for each service, which means that each service must define their own copy of any objects passed across the queue.
We are using Jackson2JsonMessageConverter on both the producer and the consumer side and we are using the Java DSL to wire the flows to/from the queues.
I am sure there is a way to do this, but it is escaping me: I want the consumer side to ignore the __TypeID__ header that is passed from the producer, as the consumer may have a different representation of that event (and it will likely be in in a different java package).
It appears there was work done such that if using the annotation #RabbitListener, an inferredArgumentTypeargument is derived and will override the header information. This is exactly what I would like to do, but I would like to use the Java DSL to do it. I have not yet found a clean way in which to do this and maybe I am just missing something obvious. It seems it would be fairly straight forward to derive the type when using the following DSL:
return IntegrationFlows
.from(
Amqp.inboundAdapter(factory, queueRemoteTaskStatus())
.concurrentConsumers(10)
.errorHandler(errorHandler)
.messageConverter(messageConverter)
)
.channel(channelRemoteTaskStatusIn())
.handle(listener, "handleRemoteTaskStatus")
.get();
However, this results in a ClassNotFound exception. The only way I have found to get around this, so far, is to set a custom message converter, which requires explicit definition of the type.
public class ForcedTypeJsonMessageConverter extends Jackson2JsonMessageConverter {
ForcedTypeJsonMessageConverter(final Class<?> forcedType) {
setClassMapper(new ClassMapper() {
#Override
public void fromClass(Class<?> clazz, MessageProperties properties) {
//this class is only used for inbound marshalling.
}
#Override
public Class<?> toClass(MessageProperties properties) {
return forcedType;
}
});
}
}
I would really like this to be derived, so the developer does not have to really deal with this.
Is there an easier way to do this?
The simplest way is to configure the Jackson converter's DefaultJackson2JavaTypeMapper with TypeIdMapping (setIdClassMapping()).
On the sending system, map foo:com.one.Foo and on the receiving system map foo:com.two.Foo.
Then, the __TypeId__ header gets foo and the receiving system will map it to its representation of a Foo.
EDIT
Another option would be to add an afterReceiveMessagePostProcessor to the inbound channel adapter's listener container - it could change the __TypeId__ header.
I have a stateless REST API build on Spring Boot 1.4.2. I want to log all the API calls into elk. Requests and responses data (headers, parameters, payload) need to be logged as well. I don't want to log them 1:1 - I want to filter out sensitive data etc.
I made an aspect that is intercepting my #RestController's methods invocation. I created custom annotation for method's parameter that should be logged (I use it on payloads annotated as well by #RequestBody) following this article and it gave me access to my data transfer objects in my #Around advice. I dont care about their type - I would like to call logger.debug(logObject) and send this log to logstash.
As far as I understand log message should be send as JSON with JSONLayout set in Log4j2 appender to ease things on the logstash side. So I serialize my logObject into JSON log message but during this and this only serialization I want to filter sensitive data out. I can not use transient because my controller depends on the same field.
Can I somehow create an #IgnoreForLogging annotation, that will be detected only by my custom Gson serializer that I use within logging advice and will be ignored within standard Spring's infrastructure? Is my logging into logstash approach even correct (I am trying to set it up for the first time)?
I can't believe I missed that in documentation. Here is the link
My custom annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface IgnoreForLogging {
}
Strategy for serializing objects:
public class LoggingExclusionStrategy implements ExclusionStrategy {
#Override
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
return fieldAttributes.getAnnotation(IgnoreForLogging.class) != null;
}
#Override
public boolean shouldSkipClass(Class<?> aClass) {
return false;
}
}
Serializing log message in aspect class:
Gson gson = new GsonBuilder()
.setExclusionStrategies(new LoggingExclusionStrategy())
.create();
String json = gson.toJson(logObject);
This way Spring internally uses default serializer that doesn't know about #IgnoreForLogging and I can take advantage of my annotation in other places.
Given a MessageChannel or Message object, how is it possible to get from one of them the name of the underlying JMS Queue which the message was received on ?
Here is the scenario:
Several jms:message-driven-channel-adapter instances are defined in the xml. The destination-name of each adapter uses SEL to receive from different queues. This SEL is dynamic, and is not possible to know these queue names ahead of time. All channel adapters output to the same internal Spring Integration channel.
I want to add the actual underlying queue name which the message was received on to the header of the message.
The idea is to setup a ChannelInterceptor for either the channel-adapters or the internal channel. The postReceive() method has both the Message and MessageChannel as arguments. Using either of these, is it possible to get the name of the underlying Queue name which the message came in on?
Thanks
Looks like you need to extend a bit DefaultJmsHeaderMapper:
class DestinationJmsHeaderMapper extends DefaultJmsHeaderMapper {
public Map<String, Object> toHeaders(javax.jms.Message jmsMessage) {
Map<String, Object> headers = super.toHeaders(jmsMessage);
headers.put("JMS_DESTINATION", ((Queue) jmsMessage.getJMSDestination()).getQueueName());
}
}
And inject it to your <jms:message-driven-channel-adapter>s
This is how we did it:
<int:header-enricher>
<int:header name="JMS_DESTINATION" expression="payload.JMSDestination.queueName"/>
</int:header-enricher>
It requires extract-payload="false" in your <jms:message-driven-channel-adapter>.
P.S. The answer of Artem is missing the return statement (I do not have enough reputations to comment).
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)