Announcement Announcement Module
Collapse
No announcement yet.
Custom message header did not pass to server Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Custom message header did not pass to server

    Hi,

    I tried to add a custom header to the message on the client side.

    Code:
    	<context:component-scan base-package="com.barnesandnoble.message"/>
    
        <beans:bean id="tcpSerializer"
              class="org.springframework.integration.ip.tcp.serializer.ByteArrayLengthHeaderSerializer">
                <beans:property name="maxMessageSize" value="20480"/>
        </beans:bean>
    
        <beans:bean id="tcpDeserializer"
              class="org.springframework.integration.ip.tcp.serializer.ByteArrayLengthHeaderSerializer">
            <beans:property name="maxMessageSize" value="20480"/>
        </beans:bean>
    
    	<ip:tcp-connection-factory id="connectionFactory"
            serializer="tcpSerializer"
            deserializer="tcpDeserializer"
    		type="client"
    		host="${dev.message.client.message.bus.host}"
    		port="${dev.message.client.message.bus.port}"
            using-nio="true"
            single-use="true"
    		so-timeout="10000"/>
    
    	<channel id="requestChannel"/>
        <channel id="replyChannel"/>
    
     	<ip:tcp-outbound-gateway id="outGateway"
    		request-channel="requestChannel"
    		reply-channel="replyChannel"
    		connection-factory="connectionFactory"
    		request-timeout="10000"
    		reply-timeout="10000"/>
    
     	<gateway id="productGateway"
    			default-request-channel="requestChannel"
                default-reply-channel="replyChannel"
    			service-interface="com.barnesandnoble.message.server.iface.ProductService">
    	</gateway>
    and the product service is defined as

    Code:
    public interface ProductService {
    
       @Gateway(requestChannel="requestChannel")
        void post(String productsInJSON, @Header(MessageHeader.STAGE) String stage);
    
    }
    The client sent the message with the custom header

    Code:
        productService.post(request, StageType.BASE.name());
    I can see the custom header in the log

    DEBUG: com.barnesandnoble.message.server.impl.ProductMess ageClient - Send request: {"products":[{"type":"BOOKS","ean":"2111251351325"}]}
    DEBUG: org.springframework.integration.channel.DirectChan nel - preSend on channel 'requestChannel', message: [Payload={"products":[{"type":"BOOKS","ean":"2111251351325"}]}][Headers={timestamp=1297966333904, id=f1b8c089-1ea4-450d-ab85-1c6c531a3021, stage=BASE}]
    DEBUG: org.springframework.integration.ip.tcp.TcpOutbound Gateway - org.springframework.integration.ip.tcp.TcpOutbound Gateway#0 received message: [Payload={"products":[{"type":"BOOKS","ean":"2111251351325"}]}][Headers={timestamp=1297966333904, id=f1b8c089-1ea4-450d-ab85-1c6c531a3021, stage=BASE}]
    DEBUG: org.springframework.integration.ip.tcp.connection. TcpNioClientConnectionFactory - Opening new socket channel connection to localhost:8888
    DEBUG: org.springframework.integration.ip.tcp.TcpOutbound Gateway - Added localhost:8888:1517879698
    DEBUG: org.springframework.integration.ip.tcp.connection. TcpNioConnection - Message sent [Payload={"products":[{"type":"BOOKS","ean":"2111251351325"}]}][Headers={timestamp=1297966333904, id=f1b8c089-1ea4-450d-ab85-1c6c531a3021, stage=BASE}]
    But the problem is the server always complained there is no header stage.

    DEBUG - _initHandlerFor_stageRouter received message: [Payload=Products{Product{ean='2111251351325', type='BOOKS'} }][Headers={timestamp=1297966333924, id=a1defcec-7fe5-48f3-a6da-fef8d2c24e79, errorChannel=org.springframework.integration.core. MessagingTemplate$TemporaryReplyChannel@11613fe7, history=tcpProductGateway,requestChannel,productPu blishChannel, ip_address=127.0.0.1, replyChannel=org.springframework.integration.core. MessagingTemplate$TemporaryReplyChannel@11613fe7, ip_connection_seq=1, ip_hostname=localhost.localdomain, ip_tcp_remote_port=48629, ip_connection_id=localhost.localdomain:48629:43077 1672}]
    2011-02-17 13:12:13,926 (pool-1-thread-15) [TcpInboundGateway.doSendAndReceive]
    WARN - failure occurred in gateway sendAndReceive
    org.springframework.integration.MessageHandlingExc eption: java.lang.IllegalArgumentException: required header not available: stage
    at org.springframework.integration.handler.MethodInvo kingMessageProcessor.processMessage(MethodInvoking MessageProcessor.java:76)
    The server configuration is as follows.

    Code:
        <beans:bean id="tcpSerializer"
              class="org.springframework.integration.ip.tcp.serializer.ByteArrayLengthHeaderSerializer">
                <beans:property name="maxMessageSize" value="${message.bus.max.message.size}"/>
        </beans:bean>
    
        <beans:bean id="tcpDeserializer"
              class="org.springframework.integration.ip.tcp.serializer.ByteArrayLengthHeaderSerializer">
            <beans:property name="maxMessageSize" value="${message.bus.max.message.size}"/>
        </beans:bean>
    
        <message-history tracked-components="*Gateway, *Channel, *Adapter"/>
    
    	<ip:tcp-connection-factory id="gatewayConnectionFactory"
            serializer="tcpSerializer"
            deserializer="tcpDeserializer"
    		type="server"
            pool-size="${message.bus.server.gateway.connection.pool.size}"
            using-nio="true"
            single-use="true"
    		port="${message.bus.server.gateway.tcp.port}"/>
    
    	<ip:tcp-inbound-gateway id="tcpProductGateway"
    		connection-factory="gatewayConnectionFactory"
    		request-channel="requestChannel"
            reply-channel="replyChannel"
    		error-channel="errorChannel"/>
    BTW, I used Wireshark to check the tcp stream, but could not find any header information.

    Do you know what was wrong here?

    Thanks,

    John

  • #2
    Yes, only the payload is transferred over TCP; this is mentioned in the documentation several times, including in the section 'TCP Message Correlation'...

    One goal of the IP Endpoints is to provide communication with systems other than another Spring
    Integration application. For this reason, only message payloads are sent and received.
    You have several options:

    1. Enhance the payload on the client...

    Code:
    	<transformer input-channel="input" output-channel="toTcp"
    		expression="headers.STAGE + ':' + payload"/>
    ..and strip it on the server...

    Code:
    	<chain input-channel="serverBytes2StringChannel" output-channel="toSA">
    		<transformer expression="new String(payload)"/>
    		<header-enricher>
    			<header name="STAGE" expression="payload.substring(0, payload.indexOf(':'))"/>
    		</header-enricher>
    		<transformer expression="payload.substring(payload.indexOf(':')+1)"/>
    	</chain>
    2. Make your payload an object that contains your actual payload and any custom headers you want; the objects would need to be Serializable and you'd use the java serialization (De)Serializers.

    HTH
    Last edited by Gary Russell; Feb 17th, 2011, 02:27 PM.

    Comment


    • #3
      Originally posted by Gary Russell View Post
      Yes, only the payload is transferred over TCP; this is mentioned in the documentation several times, including in the section 'TCP Message Correlation'...
      That sounds a bit surprise to me. Socket communication is a light weight way to integrate systems because I don't want http servlet and JMS. I really question about the assumption in the document.

      Anyway, I add the header information to the request object and it should work now.

      Comment


      • #4
        Yes, tcp is lightweight; but there are two reasons we took this approach.

        1. We couldn't make any assumptions that the collaborating service is a Spring Integration application.
        2. We certainly didn't want to require java serialization, which is needed for any complex object graph serialization (or OXM or similar).

        If you don't care about these restrictions, it's easy to do what you want, and we maintain flexibility for those that do care.

        Here's another solution...

        Code:
        @SuppressWarnings("serial")
        public class MessageWrapper implements Serializable {
        	
        	private Message<?> message;
        
        	public MessageWrapper(Message<?> message) {
        		this.message = message;
        	}
        
        	public Message<?> getMessage() {
        		return message;
        	}
        
        }
        Code:
        <transformer input-channel="input" output-channel="toTcp"
          expression="new org.springframework.integration.samples.tcpclientserver.MessageWrapper(#root)"/>
        Code:
        <chain input-channel="serverBytes2StringChannel" output-channel="toSA">
        	<header-enricher>
        		<header name="STAGE" expression="payload.message.headers['STAGE']"/>
        	</header-enricher>
        	<transformer expression="payload.message.payload"/>		
        </chain>
        SpEL is a wonderful thing :-)

        You need to configure the java (de)serializers for it to work.

        We could consider some formalized way to send a subset of headers with the payload; if you feel strongly about it enter a new feature JIRA and if it gets some votes, we'll consider it.

        Comment

        Working...
        X