Announcement Announcement Module
Collapse
No announcement yet.
Can i use two serializers with my TCP/IP adapter ? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Can i use two serializers with my TCP/IP adapter ?

    Hi everybody,

    I have to connect a remote server using TCP/IP protocol, using such kind of frames to exchange messages :

    <ENQ>
    <ACK>
    <STX>1H|\^&<CR><ETX>E5<CR><LF>
    <ACK>
    <STX>2Q|1|^20111218-0043181^01||||||||||O<CR><ETX>0D<CR><LF>
    <ACK>
    <STX>3L|1<CR><ETX>3C<CR><LF>
    <ACK>
    <EOT>

    which is actually standard LIS1-A from CLSI (if anybody knows that... )

    It appears that my data content is encapsulated both between <STX><ETX> characters (like in ByteArrayStxEtxSerializer), followed by 2 checksum characters, as well as terminated by <CR><LF> character (like in default serializer ByteArrayCrlfSerializer).

    Can you see a simple way to use both in my TCP/IP adapter, knowing that i am directely using the TcpNioClientConnectionFactory class in my code (not the inbound-tcpip object of XML configuration) ?

    Any brilliant idea ?

    Thanks a lot

  • #2
    No; you can't use 2 deserializers - but that won't work anyway.

    The deserializer is simply a mechanism to convert from a stream of bytes to a message; it demarcates messages arriving on the stream, it is not intended for doing any sophisticated object creation.

    In your case, it looks like the message is bracketed by <ENQ>...<EOT> characters.

    What you need to do is create a custom deserializer - it will be nearly identical to https://github.com/SpringSource/spri...erializer.java, except using ENQ/EOT instead of STX/ETX.

    You could then use a <transformer/> downstream of the adapter to decode the message bytes into a Java object (or whatever). Note that the <ENQ> and <EOT> will have been removed (unless you change the deserializer to leave them in the message).

    You could add the transformer functionality to the deserializer if you wish, but it's simpler to keep the concerns separate (identifying a complete message Vs. assembling the data into an object).

    On the outbound side, construct the message (minus the <ENQ> and <EOT>) and the serializer will add them.

    Comment


    • #3
      Hi Gary, thanks a lot for your time explaning this usefull things.
      I may have been not very clear about the exchanged messages in my first post : each line is a message sent over TCP/IP, so the client first request for connection with a special character:

      <ENQ>

      then the server responds with

      <ACK>

      then the client sends

      <STX>1H|\^&<CR><ETX>E5<CR><LF>

      etc, etc...

      but your idea of creating a custom serializer/deserializer is still good. In this last message for example ( <STX>1H|\^&<CR><ETX>E5<CR><LF> )

      i have : content+checksum<CR><LF>

      where content is <STX>content2<ETX>

      i was wondering, then, if i could create a custom serializer which first applies <STX><ETX> to my message (like ByteArrayStxEtxSerializer) and then i would have to compute the checksum and add <CR><LF> footer (like ByteArrayCrlfSerializer does).

      It's like 2 serialization in a row. But i understand i have to create a custom serializer for this.

      Maybe i can inherit ByteArrayCrlfSerializer, and first make a custom StxEtx transformation before outputing the result.

      And then make the opposite in the inbound way.

      Anyway, thanks for you help, i am going to try something like this.

      Comment


      • #4
        I see - yes, starting with the CRLF (de)serializer will be good in this case.

        You will need special handling for the initial <ENQ> and the final <EOT> because they are not terminated with CRLF.

        I would suggest you just pass them in to the application as a single byte array, and reply to the EOT with a zero-length message so no data is written to the socket.

        For the <ACK> replies, you could have the app build them as single byte array messages and use the serialize method from ByteArrayRawSerializer (this will also allow the zero-length trick for the <EOT>).

        Just be aware that the (de)serializers are stateless and shared across connections so you can't put conditional code in there (such as adding instance variables to maintain state, such as whether you are expecting <ENQ> etc).

        Comment


        • #5
          Gary, lots of thanks for your very clear advices, which will probably save me both time and mistakes.
          cheers from paris !

          Comment


          • #6
            One more thing about the special behavior for <ENQ> and <EOT>, i don't really understand how to deal with them.
            I have an java object (i call a module) that has both sender (outbound adapter) and receiver (inbound adapter).
            This module is then used to send/receive messages over TCP. I created a custom serializer for handling "normal" messages, but how to send the <ENQ> and <EOT> with a ByteArrayRawSerializer if my module already has my custom one ?

            Do you think i have to create other inbound and outbound adapter, just to send those messages ? I don't really understand. I would have to switch the serializer just to send those messages. What do mean by :

            I would suggest you just pass them in to the application as a single byte array, and reply to the EOT with a zero-length message so no data is written to the socket.
            Do you mean i necessarily have to use another adapter with a ByteArrayRawSerializer ?

            Thanks!

            Comment


            • #7
              No; I meant make your custom serializer a bit smarter than the normal ones, and on the outbound side, serialize like the raw serializer...

              Code:
              public class CustomSerializer extends AbstractByteArraySerializer {
              
              	private static final byte ENQ = 0x05;
              	
              	private static final byte EOT = 0x04;
              
              	/**
              	 * Reads the data in the inputstream to a byte[]. Data must be terminated
              	 * by CRLF (\r\n). Throws a {@link SoftEndOfStreamException} if the stream
              	 * is closed immediately after the \r\n (i.e. no data is in the process of
              	 * being read). 
              	 * Deserialization is identical to CRLF, but has
              	 * special handling for Initial ENQ and Final EOT messages;
                       * Serialization is raw - no modification of payload.
              	 */
              	public byte[] deserialize(InputStream inputStream) throws IOException {
              		byte[] buffer = new byte[this.maxMessageSize];
              		int n = 0;
              		int bite;
              		if (logger.isDebugEnabled()) {
              			logger.debug("Available to read:" + inputStream.available());
              		}
              		while (true) {
              			bite = inputStream.read();
              			if (bite < 0 && n == 0) {
              				throw new SoftEndOfStreamException("Stream closed between payloads");
              			}
              			checkClosure(bite);
              			if (n > 0 && bite == '\n' && buffer[n-1] == '\r') {
              				break;
              			}
              			buffer[n++] = (byte) bite;
              			// handle special 1 byte messages that don't terminate with CRLF.
              			if (n == 1 && (bite == ENQ || bite == EOT)) {
                			        n++; // need to increment so we don't shave off the last byte below
              				break;
              			}
              			if (n >= this.maxMessageSize) {
              				throw new IOException("CRLF not found before max message length: "
              						+ this.maxMessageSize);
              			}
              		};
              		byte[] assembledData = new byte[n-1];
              		System.arraycopy(buffer, 0, assembledData, 0, n-1);
              		return assembledData;
              	}
              
              	/**
              	 * Writes the byte[] to the stream.
              	 */
              	public void serialize(byte[] bytes, OutputStream outputStream) throws IOException {
              		outputStream.write(bytes);
              		outputStream.flush();
              	}
              
              }
              That way, your module will receive an initial message with a payload 0x05, it must fully construct the reply (ACK = 0x06) and the serializer will simply write it to the socket; subsequent messages will be sent to the module (minus the terminating CRLF). Again, you reply with ACKs; finally you will receive a message with just 0x04 (EOT), to which you just reply an empty buffer (no data).

              Hope that clears it up.
              Last edited by Gary Russell; Feb 16th, 2012, 09:09 AM.

              Comment


              • #8
                Thanks a lot Gary, this is very clear.

                Comment

                Working...
                X