Coverage Report - org.mule.module.ws.construct.WSProxy
 
Classes in this File Line Coverage Branch Coverage Complexity
WSProxy
0%
0/34
0%
0/10
0
WSProxy$1
N/A
N/A
0
WSProxy$AbstractProxyRequestProcessor
0%
0/36
0%
0/16
0
WSProxy$CopyInboundToOutboundPropertiesTransformerCallback
0%
0/4
0%
0/2
0
WSProxy$DynamicWsdlProxyRequestProcessor
0%
0/28
0%
0/8
0
WSProxy$DynamicWsdlProxyRequestProcessor$1
0%
0/2
N/A
0
WSProxy$DynamicWsdlProxyRequestProcessor$2
0%
0/3
N/A
0
WSProxy$DynamicWsdlProxyRequestProcessor$3
0%
0/2
N/A
0
WSProxy$DynamicWsdlProxyRequestProcessor$WsdlAddressProvider
N/A
N/A
0
WSProxy$StaticWsdlProxyRequestProcessor
0%
0/8
0%
0/4
0
 
 1  
 /*
 2  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 3  
  * The software in this package is published under the terms of the CPAL v1.0
 4  
  * license, a copy of which has been included with this distribution in the
 5  
  * LICENSE.txt file.
 6  
  */
 7  
 package org.mule.module.ws.construct;
 8  
 
 9  
 import org.mule.MessageExchangePattern;
 10  
 import org.mule.api.MessagingException;
 11  
 import org.mule.api.MuleContext;
 12  
 import org.mule.api.MuleEvent;
 13  
 import org.mule.api.MuleException;
 14  
 import org.mule.api.MuleMessage;
 15  
 import org.mule.api.construct.FlowConstructInvalidException;
 16  
 import org.mule.api.endpoint.InboundEndpoint;
 17  
 import org.mule.api.endpoint.OutboundEndpoint;
 18  
 import org.mule.api.expression.ExpressionManager;
 19  
 import org.mule.api.expression.ExpressionRuntimeException;
 20  
 import org.mule.api.processor.MessageProcessor;
 21  
 import org.mule.api.processor.MessageProcessorChainBuilder;
 22  
 import org.mule.api.source.MessageSource;
 23  
 import org.mule.config.i18n.MessageFactory;
 24  
 import org.mule.construct.AbstractFlowConstruct;
 25  
 import org.mule.construct.processor.FlowConstructStatisticsMessageProcessor;
 26  
 import org.mule.endpoint.DynamicOutboundEndpoint;
 27  
 import org.mule.interceptor.LoggingInterceptor;
 28  
 import org.mule.interceptor.ProcessingTimeInterceptor;
 29  
 import org.mule.processor.ResponseMessageProcessorAdapter;
 30  
 import org.mule.processor.StopFurtherMessageProcessingMessageProcessor;
 31  
 import org.mule.transformer.TransformerTemplate;
 32  
 import org.mule.transformer.TransformerTemplate.TransformerCallback;
 33  
 import org.mule.util.ObjectUtils;
 34  
 import org.mule.util.StringUtils;
 35  
 
 36  
 import java.net.InetAddress;
 37  
 import java.net.URI;
 38  
 import java.net.UnknownHostException;
 39  
 
 40  
 import org.apache.commons.logging.Log;
 41  
 import org.apache.commons.logging.LogFactory;
 42  
 
 43  
 /**
 44  
  * This class is implemented to act as a Proxy for a Web Service. It listens for
 45  
  * requests on the inbound endpoint and if it encounters the "WSDL" property in the
 46  
  * address, it will fetch the WSDL from the original web service and return it back.
 47  
  * In case the wsdlFile property is set, when the WSProxyService encounters a request
 48  
  * for the wsdl, instead of fetching the WSDL from the original web service, it will
 49  
  * return back the file expressed in the property. When a normal SOAP request is
 50  
  * encountered, it will forward the call to the web service with no modifications to
 51  
  * the SOAP message. The outbound router of this class must include the address of
 52  
  * the webservice to be proxied. No need to include the method name as a parameter in
 53  
  * the address, since it will be in the SOAP message as well. Furthermore a property
 54  
  * named uriWsdl can optionally be set which as the name suggests, indicate the URL
 55  
  * of the WSDL for the service. If this property is not set, the address of the WSDL
 56  
  * will be assumed to be the value of uriWebservice followed by "?WSDL".
 57  
  */
 58  
 public class WSProxy extends AbstractFlowConstruct
 59  
 {
 60  
     private final AbstractProxyRequestProcessor proxyMessageProcessor;
 61  
     private final OutboundEndpoint outboundEndpoint;
 62  
 
 63  
     public WSProxy(String name,
 64  
                    MuleContext muleContext,
 65  
                    MessageSource messageSource,
 66  
                    OutboundEndpoint outboundEndpoint) throws MuleException
 67  
     {
 68  0
         this(name, muleContext, messageSource, outboundEndpoint, new DynamicWsdlProxyRequestProcessor(
 69  
             outboundEndpoint));
 70  0
     }
 71  
 
 72  
     public WSProxy(String name,
 73  
                    MuleContext muleContext,
 74  
                    MessageSource messageSource,
 75  
                    OutboundEndpoint outboundEndpoint,
 76  
                    String wsdlContents) throws MuleException
 77  
     {
 78  0
         this(name, muleContext, messageSource, outboundEndpoint, new StaticWsdlProxyRequestProcessor(
 79  
             wsdlContents));
 80  0
     }
 81  
 
 82  
     public WSProxy(String name,
 83  
                    MuleContext muleContext,
 84  
                    MessageSource messageSource,
 85  
                    OutboundEndpoint outboundEndpoint,
 86  
                    URI wsdlUri) throws MuleException
 87  
     {
 88  0
         this(name, muleContext, messageSource, outboundEndpoint,
 89  
             new DynamicWsdlProxyRequestProcessor(wsdlUri));
 90  0
     }
 91  
 
 92  
     private WSProxy(String name,
 93  
                     MuleContext muleContext,
 94  
                     MessageSource messageSource,
 95  
                     OutboundEndpoint outboundEndpoint,
 96  
                     AbstractProxyRequestProcessor proxyMessageProcessor) throws MuleException
 97  
     {
 98  0
         super(name, muleContext);
 99  
 
 100  0
         if (messageSource == null)
 101  
         {
 102  0
             throw new FlowConstructInvalidException(
 103  
                 MessageFactory.createStaticMessage("messageSource can't be null on: " + this.toString()),
 104  
                 this);
 105  
         }
 106  
 
 107  0
         super.setMessageSource(messageSource);
 108  
 
 109  0
         if (outboundEndpoint == null)
 110  
         {
 111  0
             throw new FlowConstructInvalidException(
 112  
                 MessageFactory.createStaticMessage("outboundEndpoint can't be null on: " + this.toString()),
 113  
                 this);
 114  
         }
 115  
 
 116  0
         this.outboundEndpoint = outboundEndpoint;
 117  0
         proxyMessageProcessor.setOutboundAddress((outboundEndpoint.getAddress()));
 118  
 
 119  0
         this.proxyMessageProcessor = proxyMessageProcessor;
 120  0
     }
 121  
 
 122  
     @Override
 123  
     protected void configureMessageProcessors(MessageProcessorChainBuilder builder)
 124  
     {
 125  0
         builder.chain(new ProcessingTimeInterceptor());
 126  0
         builder.chain(new LoggingInterceptor());
 127  0
         builder.chain(new FlowConstructStatisticsMessageProcessor());
 128  0
         builder.chain(proxyMessageProcessor);
 129  0
         builder.chain(new StopFurtherMessageProcessingMessageProcessor());
 130  0
         final TransformerTemplate copyInboundToOutboundPropertiesTransformer = new TransformerTemplate(new CopyInboundToOutboundPropertiesTransformerCallback());
 131  0
         builder.chain(copyInboundToOutboundPropertiesTransformer);
 132  0
         builder.chain(new ResponseMessageProcessorAdapter(copyInboundToOutboundPropertiesTransformer));
 133  0
         builder.chain(outboundEndpoint);
 134  0
     }
 135  
 
 136  
     @Override
 137  
     protected void validateConstruct() throws FlowConstructInvalidException
 138  
     {
 139  0
         super.validateConstruct();
 140  
 
 141  0
         if ((messageSource instanceof InboundEndpoint)
 142  
             && (!((InboundEndpoint) messageSource).getExchangePattern().equals(
 143  
                 MessageExchangePattern.REQUEST_RESPONSE)))
 144  
         {
 145  0
             throw new FlowConstructInvalidException(
 146  
                 MessageFactory.createStaticMessage("WSProxy only works with a request-response inbound endpoint."),
 147  
                 this);
 148  
         }
 149  
 
 150  0
         if (!outboundEndpoint.getExchangePattern().equals(MessageExchangePattern.REQUEST_RESPONSE))
 151  
         {
 152  0
             throw new FlowConstructInvalidException(
 153  
                 MessageFactory.createStaticMessage("WSProxy only works with a request-response outbound endpoint."),
 154  
                 this);
 155  
         }
 156  0
     }
 157  
 
 158  0
     private static final class CopyInboundToOutboundPropertiesTransformerCallback
 159  
         implements TransformerCallback
 160  
     {
 161  
         public Object doTransform(MuleMessage message) throws Exception
 162  
         {
 163  0
             for (final String inboundPropertyName : message.getInboundPropertyNames())
 164  
             {
 165  0
                 message.setOutboundProperty(inboundPropertyName,
 166  
                     message.getInboundProperty(inboundPropertyName));
 167  
             }
 168  
 
 169  0
             return message;
 170  
         }
 171  
     }
 172  
 
 173  0
     private static abstract class AbstractProxyRequestProcessor implements MessageProcessor
 174  
     {
 175  
         private static final String HTTP_REQUEST = "http.request";
 176  
         private static final String WSDL_PARAM_1 = "?wsdl";
 177  
         private static final String WSDL_PARAM_2 = "&wsdl";
 178  
         private static final String LOCALHOST = "localhost";
 179  
 
 180  0
         protected final Log logger = LogFactory.getLog(WSProxy.class);
 181  
 
 182  
         private String outboundAddress;
 183  
 
 184  
         protected void setOutboundAddress(String outboundAddress)
 185  
         {
 186  0
             this.outboundAddress = outboundAddress;
 187  0
         }
 188  
 
 189  
         public MuleEvent process(MuleEvent event) throws MuleException
 190  
         {
 191  0
             if (isWsdlRequest(event))
 192  
             {
 193  0
                 return buildWsdlResult(event);
 194  
             }
 195  
 
 196  0
             if (logger.isDebugEnabled())
 197  
             {
 198  0
                 logger.debug("Forwarding SOAP message");
 199  
             }
 200  
 
 201  0
             return event;
 202  
         }
 203  
 
 204  
         private MuleEvent buildWsdlResult(MuleEvent event) throws MuleException
 205  
         {
 206  
             try
 207  
             {
 208  0
                 String wsdlContents = getWsdlContents(event);
 209  0
                 wsdlContents = modifyServiceAddress(wsdlContents, event);
 210  0
                 event.getMessage().setPayload(wsdlContents);
 211  
 
 212  
                 // the processing is stopped so that the result is not passed through
 213  
                 // the outbound router but will be passed back as a result
 214  0
                 event.setStopFurtherProcessing(true);
 215  0
                 return event;
 216  
             }
 217  0
             catch (final Exception e)
 218  
             {
 219  0
                 throw new MessagingException(
 220  
                     MessageFactory.createStaticMessage("Impossible to retrieve WSDL for proxied service"),
 221  
                     event, e);
 222  
             }
 223  
         }
 224  
 
 225  
         private String modifyServiceAddress(String wsdlContents, MuleEvent event) throws UnknownHostException
 226  
         {
 227  
             // create a new mule message with the new WSDL
 228  0
             String inboundAddress = event.getEndpoint().getAddress();
 229  
             try
 230  
             {
 231  0
                 String substitutedAddress = outboundAddress;
 232  0
                 ExpressionManager expressionManager = event.getMuleContext().getExpressionManager();
 233  0
                 if (expressionManager.isValidExpression(outboundAddress))
 234  
                 {                    
 235  0
                     substitutedAddress = expressionManager.parse(outboundAddress, event.getMessage(), true);
 236  
                 }
 237  0
                 wsdlContents = wsdlContents.replaceAll(substitutedAddress, inboundAddress);
 238  
             }
 239  0
             catch (ExpressionRuntimeException ex)
 240  
             {
 241  0
                 logger.warn("Unable to construct outbound address for WSDL request to proxied dynamic endpoint " + outboundAddress);    
 242  0
             }
 243  
 
 244  
 
 245  0
             if (wsdlContents.indexOf(LOCALHOST) > -1)
 246  
             {
 247  0
                 wsdlContents = wsdlContents.replaceAll(LOCALHOST, InetAddress.getLocalHost().getHostName());
 248  
             }
 249  
 
 250  0
             if (logger.isDebugEnabled())
 251  
             {
 252  0
                 logger.debug("WSDL modified successfully");
 253  
             }
 254  
             
 255  0
             return wsdlContents;
 256  
         }
 257  
 
 258  
         private boolean isWsdlRequest(MuleEvent event) throws MuleException
 259  
         {
 260  
             // retrieve the original HTTP request. This will be used to check if the
 261  
             // user asked for the WSDL or a service method.
 262  0
             final String httpRequest = event.getMessage().<String> getInboundProperty(HTTP_REQUEST);
 263  
 
 264  0
             if (httpRequest == null)
 265  
             {
 266  0
                 logger.warn("WS Proxy can't rewrite WSDL for non-HTTP " + event);
 267  0
                 return false;
 268  
             }
 269  
 
 270  0
             final String lowerHttpRequest = httpRequest.toLowerCase();
 271  
 
 272  
             // check if the inbound request contains the WSDL parameter
 273  0
             return (lowerHttpRequest.indexOf(WSDL_PARAM_1) != -1)
 274  
                    || (lowerHttpRequest.indexOf(WSDL_PARAM_2) != -1);
 275  
         }
 276  
 
 277  
         protected abstract String getWsdlContents(MuleEvent event) throws Exception;
 278  
     }
 279  
 
 280  
     private static class StaticWsdlProxyRequestProcessor extends AbstractProxyRequestProcessor
 281  
     {
 282  
         private final String wsdlContents;
 283  
 
 284  
         /**
 285  
          * Instantiates a request processor that returns a static WSDL contents when
 286  
          * the proxy receives a WSDL request.
 287  
          *
 288  
          * @param wsdlContents the WSDL contents to use.
 289  
          * @throws FlowConstructInvalidException
 290  
          */
 291  
         StaticWsdlProxyRequestProcessor(String wsdlContents) throws FlowConstructInvalidException
 292  0
         {
 293  0
             if (StringUtils.isBlank(wsdlContents))
 294  
             {
 295  0
                 throw new FlowConstructInvalidException(
 296  
                     MessageFactory.createStaticMessage("wsdlContents can't be empty"));
 297  
             }
 298  
 
 299  0
             this.wsdlContents = wsdlContents;
 300  0
         }
 301  
 
 302  
         @Override
 303  
         protected String getWsdlContents(MuleEvent event) throws Exception
 304  
         {
 305  0
             if (logger.isDebugEnabled())
 306  
             {
 307  0
                 logger.debug("Serving static WSDL");
 308  
             }
 309  
 
 310  0
             return wsdlContents;
 311  
         }
 312  
     }
 313  
 
 314  0
     private static class DynamicWsdlProxyRequestProcessor extends AbstractProxyRequestProcessor
 315  
     {
 316  
         private interface WsdlAddressProvider
 317  
         {
 318  
             String get(MuleEvent event);
 319  
         }
 320  
 
 321  
         private final WsdlAddressProvider wsdlAddressProvider;
 322  
 
 323  
         /**
 324  
          * Instantiates a request processor that fetches and rewrites addresses of a
 325  
          * remote WSDL when the proxy receives a WSDL request.
 326  
          *
 327  
          * @param wsdlUri the URI to fetch the WSDL from.
 328  
          * @throws FlowConstructInvalidException
 329  
          */
 330  
         DynamicWsdlProxyRequestProcessor(final URI wsdlUri) throws FlowConstructInvalidException
 331  0
         {
 332  0
             if (wsdlUri == null)
 333  
             {
 334  0
                 throw new FlowConstructInvalidException(
 335  
                     MessageFactory.createStaticMessage("wsdlUri can't be null"));
 336  
             }
 337  
 
 338  0
             final String wsdlAddress = wsdlUri.toString();
 339  
 
 340  0
             wsdlAddressProvider = new WsdlAddressProvider()
 341  0
             {
 342  
                 public String get(MuleEvent event)
 343  
                 {
 344  0
                     return wsdlAddress;
 345  
                 }
 346  
             };
 347  
 
 348  0
             logger.info("Using url " + wsdlAddress + " as WSDL");
 349  0
         }
 350  
 
 351  
         /**
 352  
          * Instantiates a request processor that fetches and rewrites addresses of a
 353  
          * remote WSDL when the proxy receives a WSDL request.
 354  
          *
 355  
          * @param outboundEndpoint the endpoint to fetch the WSDL from.
 356  
          * @throws FlowConstructInvalidException
 357  
          */
 358  
         DynamicWsdlProxyRequestProcessor(OutboundEndpoint outboundEndpoint)
 359  
             throws FlowConstructInvalidException
 360  0
         {
 361  0
             if (outboundEndpoint == null)
 362  
             {
 363  0
                 throw new FlowConstructInvalidException(
 364  
                     MessageFactory.createStaticMessage("outboundEndpoint can't be null"));
 365  
             }
 366  
 
 367  0
             final String wsAddress = outboundEndpoint.getAddress();
 368  
 
 369  0
             if (outboundEndpoint instanceof DynamicOutboundEndpoint)
 370  
             {
 371  0
                 wsdlAddressProvider = new WsdlAddressProvider()
 372  0
                 {
 373  
                     public String get(MuleEvent event)
 374  
                     {
 375  0
                         final String resolvedWsAddress = event.getMuleContext().getExpressionManager().parse(
 376  
                             wsAddress, event.getMessage(), true);
 377  
 
 378  0
                         return makeWsdlAddress(resolvedWsAddress);
 379  
                     }
 380  
                 };
 381  
 
 382  0
                 logger.info("Using dynamic WSDL with service address: " + wsAddress);
 383  
             }
 384  
             else
 385  
             {
 386  0
                 final String wsdlAddress = makeWsdlAddress(wsAddress);
 387  
 
 388  0
                 wsdlAddressProvider = new WsdlAddressProvider()
 389  0
                 {
 390  
                     public String get(MuleEvent event)
 391  
                     {
 392  0
                         return wsdlAddress;
 393  
                     }
 394  
                 };
 395  
 
 396  0
                 logger.info("Setting WSDL address to: " + wsdlAddress);
 397  
             }
 398  0
         }
 399  
 
 400  
         private static String makeWsdlAddress(String wsAddress)
 401  
         {
 402  0
             return StringUtils.substringBefore(wsAddress, "?").concat("?wsdl");
 403  
         }
 404  
 
 405  
         @Override
 406  
         protected String getWsdlContents(MuleEvent event) throws Exception
 407  
         {
 408  0
             final String wsdlAddress = wsdlAddressProvider.get(event);
 409  
             String wsdlString;
 410  
 
 411  0
             final MuleContext muleContext = event.getMuleContext();
 412  0
             final InboundEndpoint webServiceEndpoint =
 413  
                 muleContext.getEndpointFactory().getInboundEndpoint(wsdlAddress);
 414  
 
 415  0
             if (logger.isDebugEnabled())
 416  
             {
 417  0
                 logger.debug("Retrieving WSDL from web service with: " + webServiceEndpoint);
 418  
             }
 419  
 
 420  0
             final MuleMessage replyWSDL = webServiceEndpoint.request(event.getTimeout());
 421  0
             wsdlString = replyWSDL.getPayloadAsString();
 422  
 
 423  0
             return wsdlString;
 424  
         }
 425  
     }
 426  
 
 427  
     @Override
 428  
     public String toString()
 429  
     {
 430  0
         return ObjectUtils.toString(this);
 431  
     }
 432  
 
 433  
     @Override
 434  
     public String getConstructType()
 435  
     {
 436  0
         return "Web-Service-Proxy";
 437  
     }
 438  
 }