1 Reply Latest reply on Aug 24, 2011 7:07 AM by Asankha Perera

    CBR on SOAP and Transport Headers

    Asankha Perera Newbie

      I want to perform a CBR based on the value of a SOAP header (Simple String comparison) and an HTTP transport level header, and also throw a SOAP fault

       

      1. Incoming SOAP message has a SOAP Header "routing" under some name space say "http://someuri". The value is a simple String. I want to check if this is equal to some value say "gold" and route it to a specific endpoint, or else throw a SOAP fault back.

       

      2. Incoming SOAP message has a HTTP transport header "routing". I want to check if this is equal to some value say "gold" and route it to a specific endpoint, or else throw a SOAP fault back.

       

      3. I've not yet found a way to throw a SOAP fault back as well to the user, if something goes wrong. Is there an Action already available to do this without writing custom code?

       

      Any help is very much appreciated

        • 1. Re: CBR on SOAP and Transport Headers
          Asankha Perera Newbie

          I managed to solve this question myself after some effort - and I'd like to share it here for the benefit of others in future

           

          For Case #1 a normal XPath based CBR was sufficient as a better solution does not seem to exist out of the box

           

          <action name="proxy_cbr_soap_header"

              class="org.jboss.soa.esb.actions.ContentBasedRouter">

              <property name="cbrAlias" value="XPath"/>

              <!-- This is what the documentation [Services_Guide.pdf Section 3.2.4 Namespaces] says - but it does not work

              <property name="namespaces">

                  <route-to prefix="s" uri="http://schemas.xmlsoap.org/soap/envelope/" />

                  <route-to prefix="ns" uri="http://someuri" />

              </property-->

           

              <property name="destinations">

                  <namespace prefix="s" uri="http://schemas.xmlsoap.org/soap/envelope/"/>

                  <namespace prefix="ns" uri="http://someuri"/>

                  <route-to service-category="service"

                      service-name="RealService" expression="/s:Envelope/s:Header/ns:routing = 'xadmin;server1;community#1.0##'" />

                  <route-to service-category="service"

                      service-name="ReturnFault" expression="true()"/>

              </property>

          </action>

           

          Note: the documentation asks you to specify the namespaces in one way, but what works is a different configuration

           

          For case #2 I wrote a custom action as follows:

           

          Class TransportHeaderBasedRouter.java

           

          import java.util.*;

          import org.jboss.soa.esb.message.*;

          import org.apache.log4j.Logger;

          import org.jboss.soa.esb.actions.*;

          import org.jboss.soa.esb.Service;

          import org.jboss.soa.esb.ConfigurationException;

          import org.jboss.soa.esb.helpers.ConfigTree;

          import org.jboss.soa.esb.services.registry.RegistryException;

          import org.jboss.soa.esb.services.routing.MessageRouterException;

          import org.jboss.soa.esb.message.mapping.ObjectMapper;

          import org.jboss.soa.esb.message.mapping.ObjectMappingException;

          import org.jboss.soa.esb.http.HttpRequest;

          import org.jboss.soa.esb.client.MessageMulticaster;

          import org.jboss.soa.esb.listeners.ListenerTagNames;

          import java.util.regex.Pattern;

           

          /**

          * Based on JBoss ESB samples and code base

          */

          public class TransportHeaderBasedRouter extends ContentBasedWiretap {

           

              private Map<Pattern, Service> regexToServiceMap = new HashMap<Pattern, Service>();

           

              protected List<Service> executeRules(Message message) throws MessageRouterException {

           

                  String value = HttpRequest.getRequest(message).getHeaderValue(_config.getAttribute("name"));

                  List<Service> outgoingDestinations = new ArrayList<Service>();

           

                  for (Map.Entry<Pattern, Service> e : regexToServiceMap.entrySet()) {

                      if (e.getKey().matcher(value).matches()) {

                          outgoingDestinations.add(e.getValue());

                          break;

                      }

                  }

                  return outgoingDestinations;

              }

           

              public TransportHeaderBasedRouter(ConfigTree _config) throws ConfigurationException, RegistryException, MessageRouterException {

                  super(_config);

           

                  ConfigTree[] destList = _config.getChildren(ROUTE_TO_TAG);

                  if (destList != null) {

                      for (ConfigTree curr : destList) {

                          try {

                              String key = curr.getAttribute("regex");

                              String category = curr.getAttribute(ListenerTagNames.SERVICE_CATEGORY_NAME_TAG, "");

                              String name = curr.getRequiredAttribute(ListenerTagNames.SERVICE_NAME_TAG);

                              Service service = new Service(category, name);

                              regexToServiceMap.put(Pattern.compile(key), service);

                          } catch (Exception e) {

                              throw new ConfigurationException("Problems with destination list", e);

                          }

                      }

                  }

              }

           

              public Message process(Message message) throws ActionProcessingException {

                  super.process(message) ;

                  return null ;

              }

          }

           

          Invocation Snippet

           

          <action name="proxy_cbr_transport_header" class="TransportHeaderBasedRouter">

              <property name="name" value="routing"/>

              <property name="destinations">

                  <route-to service-category="service" service-name="TrpRealService" regex="xadmin;server1;community#1.0##"/>

                  <route-to service-category="service" service-name="TrpReturnFault" regex=".*"/>

              </property>

          </action>

           

          The "routing" in the above example is the HTTP header name, which is matched as a regular expression against the regex values under destinations. The first matching service is selected

           

           

          For case #3 - the easiest way I found was to write another custom action as follows:

           

          Class SOAPFaultAction.java

           

          import org.jboss.soa.esb.actions.AbstractActionLifecycle;

          import org.jboss.soa.esb.helpers.ConfigTree;

          import org.jboss.soa.esb.message.*;

          import org.jboss.soa.esb.actions.ActionProcessingException;

          import java.util.Set;

           

          public class SOAPFaultAction extends AbstractActionLifecycle {

           

              protected ConfigTree _config;

           

              public SOAPFaultAction(ConfigTree config) { _config = config; } 

              

              public Message process(Message msg) throws ActionProcessingException {

                  

                  msg.getBody().add("<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\"><S:Body><SOAP-ENV:Fault xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"><faultcode xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns=\"\">SOAP-ENV:Server</faultcode><faultstring xmlns=\"\">" + _config.getAttribute("faultText") + "</faultstring></SOAP-ENV:Fault></S:Body></S:Envelope>");

                  return msg;

              }

          }

           

          Invocation Snippet

          <action name="fault" class="SOAPFaultAction">

               <property name="faultText" value="Invalid routing header"/>

          </action>

           

          The "value" will be inserted into the custom SOAP fault specified as a String - I know its not elegant , maybe a better solution exists