Announcement Announcement Module
Collapse
No announcement yet.
Howto override the ChatMessageSendingMessageHandler in the xmpp namespace Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Howto override the ChatMessageSendingMessageHandler in the xmpp namespace

    Hello,

    I would like to inject a custom ChatMessageSendingMessageHandler implementation, which copies some custom headers to the outgoing xmpp smack message's properties.

    I have created the following classes:
    Code:
    public class ChatMessageWithCustomHeadersOutboundChannelAdapterParser
        extends AbstractXmppOutboundChannelAdapterParser {
    
      @Override
    	protected String getHandlerClassName() {
        		return "org.springframework.integration.xmpp.outbound.MyCustomChatMessageSendingMessageHandler";
    	}
    
    }
    and

    Code:
    public class MyXmppIntegrationNameSpaceHandler extends AbstractIntegrationNamespaceHandler {
     
      @Override public void init() {
        // I have tried both outbound-channel-adapter and xmpp-outbound-adapter
        registerBeanDefinitionParser( "xmpp-outbound-adapter",
                                      new ChatMessageWithCustomHeadersOutboundChannelAdapterParser() );
      }
    }
    In my integration-config.xml, I have added following entry:

    Code:
    <bean id="xmppOutboundParserOverrideBean" class="com.foo.MyXmppIntegrationNameSpaceHandler"/>
    But none of this seems to work for me. Any idea what I might be doing wrong?

  • #2
    Are you using 'xmppOutboundParserOverrideBean' as a service-activator?

    Also, could you please explain your use case. The XMPP is relatively new module and what you trying to do on your own might actually be a missing feature, so it would be nice to know what you are trying to do.

    Comment


    • #3
      Nope, I (think I) am not using it as a serviceActivator.

      This is part of my integration-config.xml:

      Code:
      <xmpp:xmpp-connection id="xmppConnection" user="${user}" password="${password}" host="${host}" port="${port}"
                              resource="${resource}" subscription-mode="accept_all"/>
      
      <integration:channel id="xmppOutbound"/>
      
      <xmpp:outbound-channel-adapter channel="xmppOutbound" xmpp-connection="xmppConnection"/>
      In our application (client) we add some headers to the Smack Message using it's property map. When the message flows into the server code (via the xmpp:inbound-channel-adapter), we pass it to the following component, which creates a org.springframework.integration.Message from the Smack Message.

      Code:
      public class XMPPMessageMapper {
      
        @ServiceActivator
        public org.springframework.integration.Message mapXMPPMessageToCanonicalMessage( Message aMessage ) {
          Map<String, Object> headers = new HashMap();
      
          headers.put( FROM, aMessage.getFrom() );
          headers.put( SUBJECT, aMessage.getSubject() );
          headers.put( TO, aMessage.getTo() );
      
          headers.put( FOO_1, aMessage.getProperty( FOO_1 ) );
          headers.put( BAR_1, aMessage.getProperty( BAR_1 ) );
      
          return new GenericMessage<String>( aMessage.getBody(), headers );
        }
      }
      I agree that the 1st 3 headers could be handled by the framework because these are real XMPP headers. However, what's important to me are the FOO_1 and BAR_1 headers.

      After this @ServiceActivator, the message flows through the system, business logic is triggered, and a response message is created (which might set some additional FOO_2 headers), which, once again, flows through the system.

      For me, it is important that these FOO and BAR headers on the org.springframework.integration.Message, are translated to properties on the org.jivesoftware.smack.packet.Message , e.g. mySmackmessage.setProperty( CustomMessageHeaders.FOO_1, "foo_1");

      When I inspect the source code of the ChatMessageSendingMessageHandler, it is clear that nothing is done with the headers, except CHAT_THREAD_ID and CHAT_TO.

      Therefore, I first wanted to extend this class (not a good idea because I cannot do a call to super method because of this line: this.xmppConnection.sendPacket(xmppMessage); )

      My second thought was to literally copy the code of this class, put it in a custom class, adapt it so it copies all headers, and then wire it into the application context. Thus I am actually looking for a clean way to override the default ChatMessageSendingMessageHandler.

      I hope this clarifies the context of my question.

      Comment


      • #4
        I'd suggest to open a JIRA improvement request - "Add support for mapping custom headers". Don't forget to attach a link to this thread.

        But for now 'ChatMessageSendingMessageHandler' is just another MessageHandler so instead of creating:

        <xmpp:outbound-channel-adapter channel="xmppOutbound" xmpp-connection="xmppConnection"/>

        you can simply use service-activator with 'ref' pointing to that custom implementation of ChatMessageSendingMessageHandler that you have

        Comment


        • #5
          The solution

          Oleg, thanks for helping me!

          I have filed a jira improvement request INT-2073 (which can be found here).

          The solution was indeed to replace the outbound-channel-adapter
          Code:
            <xmpp:outbound-channel-adapter channel="xmppOutbound" xmpp-connection="xmppConnection"/>
          with following declaration:

          Code:
            <integration:service-activator input-channel="xmppOutbound" ref="chatMessageWithCustomHeadersSendingMessageHandler">
              <constructor-arg ref="xmppConnection"/>
            </integration:service-activator>
          and to create the following class (which is allmost a literal copy of ChatMessageSendingMessageHandler):

          Code:
          @Component
          public class ChatMessageWithCustomHeadersSendingMessageHandler extends AbstractXmppConnectionAwareMessageHandler {
          
            public ChatMessageWithCustomHeadersSendingMessageHandler() {
          		super();
          	}
          
          	public ChatMessageWithCustomHeadersSendingMessageHandler(XMPPConnection xmppConnection) {
          		super(xmppConnection);
          	}
          
          
          	@Override
            @ServiceActivator
          	protected void handleMessageInternal(Message<?> message) throws Exception {
          		Assert.isTrue( this.initialized, this.getComponentName() + "#" + this.getComponentType() + " must be initialized" );
          		Object messageBody = message.getPayload();
          		org.jivesoftware.smack.packet.Message xmppMessage = null;
          		if (messageBody instanceof org.jivesoftware.smack.packet.Message) {
          			xmppMessage = (org.jivesoftware.smack.packet.Message) messageBody;
          		}
          		else if (messageBody instanceof String) {
          			String chatTo = message.getHeaders().get( XmppHeaders.CHAT_TO, String.class);
          			Assert.state( StringUtils.hasText( chatTo ), "The '" + XmppHeaders.CHAT_TO + "' header must not be null");
          			xmppMessage = new org.jivesoftware.smack.packet.Message(chatTo);
          			String threadId = message.getHeaders().get(XmppHeaders.CHAT_THREAD_ID, String.class);
          			if (StringUtils.hasText(threadId)) {
          				xmppMessage.setThread(threadId);
          			}
          
                Iterator<String> headerKeysIterator = message.getHeaders().keySet().iterator();
                while(headerKeysIterator.hasNext()) {
                  String headerKey = headerKeysIterator.next();
                  if(!headerKey.equalsIgnoreCase( XmppHeaders.CHAT_TO ) &&
                     !headerKey.equalsIgnoreCase( XmppHeaders.CHAT_THREAD_ID )) {
                    xmppMessage.setProperty( headerKey , message.getHeaders().get( headerKey ) );
                  }
                }
          
          			xmppMessage.setBody((String) messageBody);
          		}
          		else {
          			throw new MessageHandlingException(message, "Only payloads of type java.lang.String or org.jivesoftware.smack.packet.Message " +
          					"are supported. Received [" + messageBody.getClass().getName() +
          					"]. Consider adding a Transformer prior to this adapter.");
          		}
          		if (!this.xmppConnection.isConnected()) {
          			this.xmppConnection.connect();
          		}
          		this.xmppConnection.sendPacket(xmppMessage);
          	}
          }
          Note: Bear in mind that this is a (too) simple solution, it might copy more headers than desired because it copies them all!
          Last edited by davidcyp; Aug 22nd, 2011, 08:55 AM. Reason: replaced placeholder with classname

          Comment


          • #6
            I've started work on this here: https://github.com/markfisher/spring.../tree/INT-2073

            The goal will be to enable the headers to be mapped to be configurable like we do for HTTP (including wildcard options). The refactoring will also affect other header-mappers for consistency (e.g. AMQP). The target is now 2.1 RC1.

            Comment

            Working...
            X