Hi, Could you please help me with understanding of message transformation in switchyard and how I could set the SOAP message header? Because, I'm quite confused in how it really works. The aim all of this would be a support to propagate a transaction context in the SOAP message header. There is a JIRA https://issues.jboss.org/browse/SWITCHYARD-703
I have a content of message, the dom element and map of header parts. As I looked, org.switchyard.Message contains only a content and attachments. In this start state the content of the message is not a SOAP message but only a part of it (an operation). This message is sent by Exchange. I noticed that in ExchangeImpl it is somehow transformed and sent somewhere. I found that there exists a org.switchyard.component.soap.DefaultMessageDecomposer that creates a SOAPMessage from the switchyard Message, but It don't do anything with header. Is the header anyhow joined after a decompose anywhere or it's not implemented yet?
Scenario is that I will invoke WS from BPEL. RiftSaw, the BPEL engine will propagate the message content and header parts into Switchyard BPEL component and from there It should be transfered into SOAP message with the header and sent to the WS.
Thank you, Ivo
You should look at MessageComposer and ContextMapper. The MessageComposer works in conjunction with the ContextMapper, which manages how header properties are mapped. For the default SOAP message translation see SOAPMessageComposer and SOAPContextMapper in the switchyard-component-soap project. Those should give you some idea of how the raw messages are translated into SwitchYard messages.
You can use the following elements in your switchyard.xml binding declaration to specify your custom composer/mapper:
Hope that helps,
Oh, thank you, I see it already , the MessageComposer is for the body and ContextMapper for MIME and the header. So, It should be enough to set the context in Exchange with Scope.EXCHANGE properties for the header. Do you know please, how can I set the nested properties? The SOAPContextMapper doesn't use anything to put them into header. For example a registrationService has the problem, I need to put this to the header:
<CoordinationContext xmlns="http://docs.oasis-open.org/ws-tx/wscoor/2006/06" xmlns:ns2="http://www.w3.org/2005/08/addressing">
<wsarj:InstanceIdentifier xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:wsarj="http://schemas.arjuna.com/ws/2005/10/wsarj">0:ffff7f000001:76ef4aab:4f662292:42</wsarj:InstanceIdentifier>
<wsam:ServiceName xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:wscoor="http://docs.oasis-open.org/ws-tx/wscoor/2006/06" EndpointName="RegistrationPortType">wscoor:RegistrationService</wsam:ServiceName>
Any context properties you put in will be opaque to the runtime (i.e. runtime doesn't care about them), so you can use whatever method you want. You could store the entire XML fragment as a single context property. Or you could create your own wrapper object as the key. Or you could use a Map, etc., etc. Keep in mind that you do *not* have to map this XML to an object model in order to add it to the exchange context. Also keep in mind that you should set the context property at the appropriate scope. So if the property only applies to the input message, use the IN scope. If it applies to both input and output, then use the EXCHANGE scope (which is the default).
It would be nice, but I cannot set own ContextMapper in Exchange, can I? Because, the SOAPContextMapper uses name of the property as header child node and the value of property as a content of the node. (https://github.com/jboss-switchyard/components/blob/master/soap/src/main/java/org/switchyard/component/soap/composer/SOAPContextMapper.java#L102). Thus, I should have some object which would transfer the header Node for example to the String in toString() method. I think, I miss something important.
A few things here:
- The SOAP api wants Strings. So even though you have an XML element representing a CoordinationContext, you will have to turn it into an xml String before you set it into the Context as a property. You will then obviously have to turn (parse) that String back into a CoordinationContext xml element yourself after you get it out of the Context.
- You shouldn't have to implement your own ContextMapper for what you're trying to accomplish. If you do, however, the instructions for doing so are here: https://docs.jboss.org/author/display/SWITCHYARD/Message+Composition
- It is important to know that NO Context properties are mapped by default. In your switchyard.xml for your binding, you have to include a <contextMapper/> element for any mapping to be done.
Clarrification: If you DO decide to write your own ContextMapper, then you could just set your xml elements right in/out of the switchyard Context as properties directly, without String conversion. But then you would have to add those as child elements to the SOAPElement yourself inside your custom ContextMapper. Unfortunately that means recursively working down the tree adding children via their qnames, setting their attributes, then their children or finally, their string values.
Hi David, no you were right because I cannot use the appendChild(Node) because the Node is created in another Document. I tried to implement it to work and I have it now that I use the addChildElement recursively for child nodes, but I had to change the SOAPContextMapper, where I detect a type of value. I had to do it because I wasn't able to send the header even if I put the correct header string into the soapHeader.addChildElement(qname).setValue method in SOAPContextMapper. So I have it working, but I don't know if you want me to do these changes into soap module. As I well understand, if I would want to do my own ContextMapper, a BPEL users should have it set in the binding in their apps (switchyard.xml), but I think It wouldn't be nice to have more SOAPContextMappers almost same.
Even if a Node is created by another document you can just create a new document fragment and add the node to that by importing it. The javadoc for JAXP should provide detail on how to do this. You should not have to do any recursive calls to add this stuff.
If there is an enhancement available here for the SOAP context mapper, then we should look at doing that as a general purpose solution. Perhaps there could be a basic config option on the mapper that indicates how SOAP headers should be treated, e.g. each header added as it's own doc fragment as a context property, all headers in one context property, etc.
Thank you, it really works, I wasn't sure how the import method works. Yes the configuration would be useful even for a mapFrom method, because now the method looks strange ...if I well understand, for every header part it adds property to context where the key is a name of the header part and value is always a text element of header ... so I think, It cannot work. Does anybody use the headers? I would like to change the method to put header parts into context as a Nodes, but there must be some rules how to define the keys for header parts or only a header. And then we can add the config option to indicate how it is put into or from the context. Oh and please what for is a matches method with includes and excludes? I don't know about any practical use case to eliminate the header. AFAIK, I didn't set anything, includes or excludes and I wasn't able to send the headers becase the matches method has a default return value set as false.
If you want to look at the changes what I made, you can look here https://github.com/ibek/components/compare/SWITCHYARD-703
I have already changed the test for a soap message header. And It puts the SOAP message header parts as a properties with "soapheader_" prefix. It is used in RiftsawBPELExchangeHandler and the test. What do you think, Keith?
- importNode works, but I suggest also looking at adoptNode, which changes document ownership recursively rather than making a deep copy.
- Yes, you are understanding the current behavior correctly. Up until now, the only usage of soap headers I know of are via the BPM and Rules components, which carry certain action/meta-information in them. For example, BPM's processInstanceId so that future intractions with a process can be correlated back to the proper instance. In these cases, only a String value was needed - not any kind of complex child structure (like a Node).
- I like the idea of adding some kind of configuration capability to ContextMappers to maintain Node values rather than simply using their String values. I'll create a jira.
- The matches capability is a way to use regular expressions to decide which properties/headers will be mapped by the ContextMapper. By default, everything is excluded (to reduce bloat). You have to tell it (via it's various configuration properties of includes/includesNamespaces/excludes/excludesNamespaces) which properties/headers you want included.
Hi Ivo, I looked at your changes to SOAPContextMapper, and have a couple concerns:
- I'm not a fan of the prefixing the properties with a hardcoded "soapheader_". If we go the way of using prefixes - which I'm not convinced is the Right Thing To Do, then a default prefix is okay, but there should be a way that users can configure it to their own prefix.
- Always adding the soap header part as a Node directly into the Context will mean any code that needs access to that property will always now have to deal with a Node as a value instead of a simple String. This is where having some kind of configuration defining the behavior of the ContextMapper would be useful. And I don't believe it should be done only in SOAPContextMapper.