Results 1 to 6 of 6

Thread: Howto override the ChatMessageSendingMessageHandler in the xmpp namespace

  1. #1
    Join Date
    Aug 2011
    Location
    Belgium
    Posts
    23

    Default 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. #2
    Join Date
    Jan 2008
    Location
    Mohnton, PA USA (that's near Philadelphia)
    Posts
    2,148

    Default

    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.

  3. #3
    Join Date
    Aug 2011
    Location
    Belgium
    Posts
    23

    Default

    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.

  4. #4
    Join Date
    Jan 2008
    Location
    Mohnton, PA USA (that's near Philadelphia)
    Posts
    2,148

    Default

    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

  5. #5
    Join Date
    Aug 2011
    Location
    Belgium
    Posts
    23

    Default 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 at 08:55 AM. Reason: replaced placeholder with classname

  6. #6
    Join Date
    Oct 2005
    Location
    Boston, MA
    Posts
    2,854

    Default

    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.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •